From a25366a958490a2902c815b4ad696053880aab48 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Mon, 1 Jun 2015 15:31:13 -0700 Subject: [PATCH 01/53] Initial commit --- .gitignore | 36 ++++++++++++++++++++ LICENSE | 8 +++++ README | 11 ++++++ app/controllers/Application.scala | 23 +++++++++++++ app/models/User.scala | 6 ++++ app/modules/DatabaseModule.scala | 36 ++++++++++++++++++++ app/slick/UserDAO.scala | 54 ++++++++++++++++++++++++++++++ app/views/index.scala.html | 5 +++ app/views/main.scala.html | 15 +++++++++ build.sbt | 24 +++++++++++++ conf/application.conf | 31 +++++++++++++++++ conf/evolutions/create.sql | 3 ++ conf/logback.xml | 26 ++++++++++++++ conf/routes | 9 +++++ project/build.properties | 4 +++ project/plugins.sbt | 16 +++++++++ public/images/favicon.png | Bin 0 -> 687 bytes public/javascripts/hello.js | 3 ++ public/stylesheets/main.css | 0 test/ApplicationSpec.scala | 30 +++++++++++++++++ test/IntegrationSpec.scala | 24 +++++++++++++ 21 files changed, 364 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README create mode 100644 app/controllers/Application.scala create mode 100644 app/models/User.scala create mode 100644 app/modules/DatabaseModule.scala create mode 100644 app/slick/UserDAO.scala create mode 100644 app/views/index.scala.html create mode 100644 app/views/main.scala.html create mode 100644 build.sbt create mode 100644 conf/application.conf create mode 100644 conf/evolutions/create.sql create mode 100644 conf/logback.xml create mode 100644 conf/routes create mode 100644 project/build.properties create mode 100644 project/plugins.sbt create mode 100644 public/images/favicon.png create mode 100644 public/javascripts/hello.js create mode 100644 public/stylesheets/main.css create mode 100644 test/ApplicationSpec.scala create mode 100644 test/IntegrationSpec.scala diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..ec080a7ba --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +/.idea + +# sbt specific +.cache +.history +.lib/ +dist/* +target/ +lib_managed/ +src_managed/ +project/boot/ +project/plugins/project/ + +# Scala-IDE specific +.scala_dependencies +.worksheet + +### PlayFramework template +# Ignore Play! working directory # +bin/ +/db +.eclipse +/lib/ +/logs/ +/modules +/project/project +/project/target +/target +tmp/ +test-result +server.pid +*.iml +*.eml +/dist/ + + diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..4baedcb95 --- /dev/null +++ b/LICENSE @@ -0,0 +1,8 @@ +This software is licensed under the Apache 2 license, quoted below. + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with +the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. \ No newline at end of file diff --git a/README b/README new file mode 100644 index 000000000..d13a99980 --- /dev/null +++ b/README @@ -0,0 +1,11 @@ += Play 2.4 with Slick 3.0 + +This is an example project that shows Play 2.4 working with Slick 3.0. + +It does not use Play Slick, but does use HikariCP and is set up to work with PostgreSQL. + + +``` +sudo su - postgres +createdb -U postgres myapp +``` \ No newline at end of file diff --git a/app/controllers/Application.scala b/app/controllers/Application.scala new file mode 100644 index 000000000..bfc830f12 --- /dev/null +++ b/app/controllers/Application.scala @@ -0,0 +1,23 @@ +package controllers + +import java.util.UUID +import javax.inject.Inject + +import akka.actor.ActorSystem +import models.User +import play.api._ +import play.api.mvc._ +import slick.UserDAO + +class Application @Inject() (userDAO: UserDAO, actorSystem:ActorSystem) extends Controller { + + // Set up an execution context from the akka dispatchers library... + private implicit val ec = actorSystem.dispatchers.lookup("myapp.dispatcher") + + def index = Action.async { + userDAO.create(User(UUID.randomUUID.toString, "some@example.com")).map { rows => + Ok(views.html.index(s"row = ${rows}")) + } + } + +} diff --git a/app/models/User.scala b/app/models/User.scala new file mode 100644 index 000000000..c562b386d --- /dev/null +++ b/app/models/User.scala @@ -0,0 +1,6 @@ +package models + +/** + * + */ +case class User(id:String, email:String) diff --git a/app/modules/DatabaseModule.scala b/app/modules/DatabaseModule.scala new file mode 100644 index 000000000..de41f9c12 --- /dev/null +++ b/app/modules/DatabaseModule.scala @@ -0,0 +1,36 @@ +package modules + +import javax.inject.{Inject, Singleton, Provider} + +import com.google.inject.AbstractModule +import com.typesafe.config.Config +import play.api.{Environment, Configuration} +import play.api.inject.ApplicationLifecycle + +import scala.concurrent.Future + +/** + * + */ +class DatabaseModule(environment: Environment, + configuration: Configuration) extends AbstractModule { + override def configure(): Unit = { + bind(classOf[Config]).toInstance(configuration.underlying) + bind(classOf[slick.jdbc.JdbcBackend.Database]).toProvider(classOf[DatabaseProvider]) + bind(classOf[slick.UserDAO]).asEagerSingleton() + } +} + +@Singleton +class DatabaseProvider @Inject() (config: Config, lifecycle: ApplicationLifecycle) extends Provider[slick.jdbc.JdbcBackend.Database] { + + private val db = slick.jdbc.JdbcBackend.Database.forConfig("myapp.database", config) + + lifecycle.addStopHook { () => + Future.successful { + db.close() + } + } + + override def get(): slick.jdbc.JdbcBackend.Database = db +} diff --git a/app/slick/UserDAO.scala b/app/slick/UserDAO.scala new file mode 100644 index 000000000..76eced0f4 --- /dev/null +++ b/app/slick/UserDAO.scala @@ -0,0 +1,54 @@ +package slick + +import javax.inject.{Singleton, Inject} + +import akka.actor.ActorSystem +import com.typesafe.config.Config +import models.User + +import scala.concurrent.Future +import slick.driver.PostgresDriver.api._ + +/** + * + */ +@Singleton +class UserDAO @Inject() (config:Config, db:Database) { + + import slick.driver.PostgresDriver.api._ + + private val users = TableQuery[Users] + + private val queryById = Compiled( + (id: Rep[String]) => users.filter(_.id === id)) + + + def lookup(id: String): Future[Option[User]] = { + db.run(queryById(id).result.headOption) + } + + def all: Future[Seq[User]] = { + db.run(users.result) + } + + def update(user:User) = { + db.run(queryById(user.id).update(user)) + } + + def delete(id:String) = { + db.run(queryById(id).delete) + } + + def create(user:User): Future[Int] = { + db.run( + users += user + ) + } + + class Users(tag: Tag) extends Table[User](tag, "USERS") { + def id = column[String]("ID", O.PrimaryKey) + def email = column[String]("EMAIL") + + def * = (id, email) <> (User.tupled, User.unapply) + } +} diff --git a/app/views/index.scala.html b/app/views/index.scala.html new file mode 100644 index 000000000..71a170107 --- /dev/null +++ b/app/views/index.scala.html @@ -0,0 +1,5 @@ +@(message: String) + +
+ Hello world! @message +
\ No newline at end of file diff --git a/app/views/main.scala.html b/app/views/main.scala.html new file mode 100644 index 000000000..aff0eff1d --- /dev/null +++ b/app/views/main.scala.html @@ -0,0 +1,15 @@ +@(title: String)(content: Html) + + + + + + @title + + + + + + @content + + diff --git a/build.sbt b/build.sbt new file mode 100644 index 000000000..8b8ae049b --- /dev/null +++ b/build.sbt @@ -0,0 +1,24 @@ +name := """play-slick-3.0""" + +version := "1.0-SNAPSHOT" + +lazy val root = (project in file(".")).enablePlugins(PlayScala) + +scalaVersion := "2.11.6" + +libraryDependencies ++= Seq( + "org.postgresql" % "postgresql" % "9.4-1201-jdbc41", + "com.zaxxer" % "HikariCP" % "2.3.8", + "com.typesafe.slick" %% "slick" % "3.0.0", + specs2 % Test +) + +resolvers += "scalaz-bintray" at "http://dl.bintray.com/scalaz/releases" + +resolvers += Resolver.sonatypeRepo("releases") + +resolvers += Resolver.sonatypeRepo("snapshots") + +// Play provides two styles of routers, one expects its actions to be injected, the +// other, legacy style, accesses its actions statically. +routesGenerator := InjectedRoutesGenerator diff --git a/conf/application.conf b/conf/application.conf new file mode 100644 index 000000000..178b4e660 --- /dev/null +++ b/conf/application.conf @@ -0,0 +1,31 @@ +# This is the main configuration file for the application. +# ~~~~~ + +play.crypto.secret = "changeme" + +# The application languages +# ~~~~~ +play.i18n.langs = [ "en" ] + +play.modules.enabled += "modules.DatabaseModule" + +myapp = { + database = { + driver = org.postgresql.Driver + url = "jdbc:postgresql://localhost/myapp" + user = "postgres" + password = "postgres" + numThreads = 10 + connectionTimeout = 5000 + validationTimeout = 5000 + } + + dispatcher { + fork-join-executor { + parallelism-factor = 2 + parallelism-max = 20 + } + } + +} + diff --git a/conf/evolutions/create.sql b/conf/evolutions/create.sql new file mode 100644 index 000000000..4f10c163d --- /dev/null +++ b/conf/evolutions/create.sql @@ -0,0 +1,3 @@ + + +create table "USERS" ("ID" VARCHAR NOT NULL PRIMARY KEY, "EMAIL" VARCHAR(1024)); diff --git a/conf/logback.xml b/conf/logback.xml new file mode 100644 index 000000000..599e40466 --- /dev/null +++ b/conf/logback.xml @@ -0,0 +1,26 @@ + + + + + + + %coloredLevel - %logger - %message%n%xException + + + + + + + + + + + + + + + + diff --git a/conf/routes b/conf/routes new file mode 100644 index 000000000..e71a0bd06 --- /dev/null +++ b/conf/routes @@ -0,0 +1,9 @@ +# Routes +# This file defines all application routes (Higher priority routes first) +# ~~~~ + +# Home page +GET / controllers.Application.index + +# Map static resources from the /public folder to the /assets URL path +GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset) diff --git a/project/build.properties b/project/build.properties new file mode 100644 index 000000000..a6cd27895 --- /dev/null +++ b/project/build.properties @@ -0,0 +1,4 @@ +#Activator-generated Properties +#Mon Jun 01 12:58:43 PDT 2015 +template.uuid=4dd0bfb4-5d69-4e44-a1a9-1a18a28af9b4 +sbt.version=0.13.8 diff --git a/project/plugins.sbt b/project/plugins.sbt new file mode 100644 index 000000000..f833ed639 --- /dev/null +++ b/project/plugins.sbt @@ -0,0 +1,16 @@ +// The Play plugin +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.4.0") + +// web plugins + +addSbtPlugin("com.typesafe.sbt" % "sbt-coffeescript" % "1.0.0") + +addSbtPlugin("com.typesafe.sbt" % "sbt-less" % "1.0.6") + +addSbtPlugin("com.typesafe.sbt" % "sbt-jshint" % "1.0.3") + +addSbtPlugin("com.typesafe.sbt" % "sbt-rjs" % "1.0.7") + +addSbtPlugin("com.typesafe.sbt" % "sbt-digest" % "1.1.0") + +addSbtPlugin("com.typesafe.sbt" % "sbt-mocha" % "1.1.0") diff --git a/public/images/favicon.png b/public/images/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..c7d92d2ae47434d9a61c90bc205e099b673b9dd5 GIT binary patch literal 687 zcmV;g0#N;lP)ezT{T_ZJ?}AL z5NC{NW(ESID=>(O3&Eg8 zmA9J&6c`h4_f6L;=bU>_H8aNG`kfvCj9zomNt)?O;rzWqZs0LEt%1WB218%1fo9uB zsW^yhBR7C(mqN%GEK9&msg0~ zWY?#bf4q8G-~2KttQZ($odJvy&_-~f?9*ThK@fwR$U^1)p*8=_+^3BXx0$i1BC8XC zr21u6D5nVK&^!dOAw&|1E;qC3uFNj3*Jj#&%Oje@0D-nhfmM*o%^5f}-pxQ07(95H z3|LoV>V19w#rLgmRmtVy9!T3M3FUE3><0T8&b3yEsWcLW`0(=1+qsqc(k(ymBLK0h zK!6(6$7MX~M`-QA2$wk7n(7hhkJ}4Rwi-Vd(_ZFX1Yk7TXuB0IJYpo@kLb2G8m)E{ z`9v=!hi}fOytKckfN^C@6+Z*+MVI9-W_p@_3yyR#UYc0FTpD}i#k>c!wYCS)4v@E$ zchZCo=zV@)`v^$;V18ixdjFMY#q^2$wEX%{f(XD8POnsn$bpbClpC@hPxjzyO>pY|*pF3UU2tYcCN?rUk{Sskej70Mmu9vPwMYhO1m{AxAt(zqDT|0jP7FaX=6 V`?~}E4H^Id002ovPDHLkV1hC)G==~G literal 0 HcmV?d00001 diff --git a/public/javascripts/hello.js b/public/javascripts/hello.js new file mode 100644 index 000000000..209fbee59 --- /dev/null +++ b/public/javascripts/hello.js @@ -0,0 +1,3 @@ +if (window.console) { + console.log("Welcome to your Play application's JavaScript!"); +} \ No newline at end of file diff --git a/public/stylesheets/main.css b/public/stylesheets/main.css new file mode 100644 index 000000000..e69de29bb diff --git a/test/ApplicationSpec.scala b/test/ApplicationSpec.scala new file mode 100644 index 000000000..036a94e87 --- /dev/null +++ b/test/ApplicationSpec.scala @@ -0,0 +1,30 @@ +import org.specs2.mutable._ +import org.specs2.runner._ +import org.junit.runner._ + +import play.api.test._ +import play.api.test.Helpers._ + +/** + * Add your spec here. + * You can mock out a whole application including requests, plugins etc. + * For more information, consult the wiki. + */ +@RunWith(classOf[JUnitRunner]) +class ApplicationSpec extends Specification { + + "Application" should { + + "send 404 on a bad request" in new WithApplication{ + route(FakeRequest(GET, "/boum")) must beSome.which (status(_) == NOT_FOUND) + } + + "render the index page" in new WithApplication{ + val home = route(FakeRequest(GET, "/")).get + + status(home) must equalTo(OK) + contentType(home) must beSome.which(_ == "text/html") + contentAsString(home) must contain ("Your new application is ready.") + } + } +} diff --git a/test/IntegrationSpec.scala b/test/IntegrationSpec.scala new file mode 100644 index 000000000..652edde7f --- /dev/null +++ b/test/IntegrationSpec.scala @@ -0,0 +1,24 @@ +import org.specs2.mutable._ +import org.specs2.runner._ +import org.junit.runner._ + +import play.api.test._ +import play.api.test.Helpers._ + +/** + * add your integration spec here. + * An integration test will fire up a whole play application in a real (or headless) browser + */ +@RunWith(classOf[JUnitRunner]) +class IntegrationSpec extends Specification { + + "Application" should { + + "work from within a browser" in new WithBrowser { + + browser.goTo("http://localhost:" + port) + + browser.pageSource must contain("Your new application is ready.") + } + } +} From 5ff703e345b728f011afbbb546e32adab75dd41f Mon Sep 17 00:00:00 2001 From: Felipe Almeida Date: Fri, 12 Jun 2015 21:07:49 +0000 Subject: [PATCH 02/53] activate markdown --- README | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 README diff --git a/README b/README deleted file mode 100644 index d13a99980..000000000 --- a/README +++ /dev/null @@ -1,11 +0,0 @@ -= Play 2.4 with Slick 3.0 - -This is an example project that shows Play 2.4 working with Slick 3.0. - -It does not use Play Slick, but does use HikariCP and is set up to work with PostgreSQL. - - -``` -sudo su - postgres -createdb -U postgres myapp -``` \ No newline at end of file From cf55ffdbff9564550dd56e0e5cd5f1da6eb1b230 Mon Sep 17 00:00:00 2001 From: Felipe Almeida Date: Fri, 12 Jun 2015 21:13:18 +0000 Subject: [PATCH 03/53] change name to activate markdown --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 000000000..d13a99980 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ += Play 2.4 with Slick 3.0 + +This is an example project that shows Play 2.4 working with Slick 3.0. + +It does not use Play Slick, but does use HikariCP and is set up to work with PostgreSQL. + + +``` +sudo su - postgres +createdb -U postgres myapp +``` \ No newline at end of file From 8de657db4d25173a1d6e408ac7446644820bacab Mon Sep 17 00:00:00 2001 From: Felipe Almeida Date: Fri, 12 Jun 2015 21:14:20 +0000 Subject: [PATCH 04/53] change title syntax --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d13a99980..a45d00f24 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -= Play 2.4 with Slick 3.0 +## Play 2.4 with Slick 3.0 This is an example project that shows Play 2.4 working with Slick 3.0. @@ -8,4 +8,4 @@ It does not use Play Slick, but does use HikariCP and is set up to work with Pos ``` sudo su - postgres createdb -U postgres myapp -``` \ No newline at end of file +``` From eaa2f56b3f5aa1a8cb41f7531acef0e3d2eac776 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Mon, 12 Oct 2015 17:12:58 -0700 Subject: [PATCH 05/53] Update with modularized code. --- README.md | 52 ++++++++++++++++++++++++++--- app/controllers/Application.scala | 15 +++++---- app/models/User.scala | 6 ---- app/modules/AppModule.scala | 37 +++++++++++++++++++++ app/modules/DatabaseModule.scala | 36 -------------------- app/slick/UserDAO.scala | 54 ------------------------------ build.sbt | 30 ++++++++++++----- conf/application.conf | 31 +++++++---------- conf/evolutions/create.sql | 3 -- conf/logback.xml | 55 +++++++++++++++++++++---------- project/Build.scala | 30 +++++++++++++++++ project/build.properties | 5 +-- project/plugins.sbt | 4 ++- 13 files changed, 197 insertions(+), 161 deletions(-) delete mode 100644 app/models/User.scala create mode 100644 app/modules/AppModule.scala delete mode 100644 app/modules/DatabaseModule.scala delete mode 100644 app/slick/UserDAO.scala delete mode 100644 conf/evolutions/create.sql create mode 100644 project/Build.scala diff --git a/README.md b/README.md index a45d00f24..58ad2d82d 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,55 @@ -## Play 2.4 with Slick 3.0 +## Play 2.4 with Slick 3.1 -This is an example project that shows Play 2.4 working with Slick 3.0. - -It does not use Play Slick, but does use HikariCP and is set up to work with PostgreSQL. +This is an example project that shows Play 2.4 working with a self contained Slick 3.1.0 module. It uses HikariCP with PostgreSQL 9.4. ``` sudo su - postgres createdb -U postgres myapp ``` + +Then check that "modules/slick/build.sbt" has the URL that you want, and type: + +``` +sbt flywayMigrate +``` + +This creates the tables for the Slick module. Please see the [flyways documentation](http://flywaydb.org/getstarted/firststeps/sbt.html) for more details. + + + +Note that the Slick module is self-contained and does not have any Play references. Play JSON can be used standalone. + +The module is defined with a public API in "modules/api": + +``` +package com.example.user + +trait UserDAO { + + def lookup(id: String): Future[Option[User]] + + def all: Future[Seq[User]] + + def update(user:User) + + def delete(id:String) + + def create(user:User): Future[Int] + +} +``` + +and the main Play application uses the exposed API without touching the database implementation, using Guice DI. + +There is a drawback to this model: when used in development mode, + +``` +class DatabaseProvider @Inject() (dao: UserDAO, lifecycle: ApplicationLifecycle) extends Provider[] { + lifecycle.addStopHook { () => + Future.successful { + dao.close() + } + } +} +``` diff --git a/app/controllers/Application.scala b/app/controllers/Application.scala index bfc830f12..7b9ebc0a0 100644 --- a/app/controllers/Application.scala +++ b/app/controllers/Application.scala @@ -4,20 +4,21 @@ import java.util.UUID import javax.inject.Inject import akka.actor.ActorSystem -import models.User +import com.example.user.{User, UserDAO} import play.api._ import play.api.mvc._ -import slick.UserDAO class Application @Inject() (userDAO: UserDAO, actorSystem:ActorSystem) extends Controller { - // Set up an execution context from the akka dispatchers library... - private implicit val ec = actorSystem.dispatchers.lookup("myapp.dispatcher") + private val ec = actorSystem.dispatchers.lookup("myapp.database-dispatcher") def index = Action.async { - userDAO.create(User(UUID.randomUUID.toString, "some@example.com")).map { rows => - Ok(views.html.index(s"row = ${rows}")) - } + // Set up an execution context from the akka dispatchers library... + + val id = UUID.randomUUID.toString + userDAO.create(User(id, "some@example.com")).map { rows => + Ok(views.html.index(s"Connected to database and created user $id")) + }(ec) } } diff --git a/app/models/User.scala b/app/models/User.scala deleted file mode 100644 index c562b386d..000000000 --- a/app/models/User.scala +++ /dev/null @@ -1,6 +0,0 @@ -package models - -/** - * - */ -case class User(id:String, email:String) diff --git a/app/modules/AppModule.scala b/app/modules/AppModule.scala new file mode 100644 index 000000000..547182e3b --- /dev/null +++ b/app/modules/AppModule.scala @@ -0,0 +1,37 @@ +package modules + +import javax.inject.Inject + +import com.example.user.{UserDAO, SlickUserModule} +import com.google.inject.AbstractModule +import com.typesafe.config.Config +import play.api.inject.ApplicationLifecycle + +import play.api.{Environment, Configuration} + +import scala.concurrent.Future + +class AppModule(environment: Environment, + configuration: Configuration) extends AbstractModule { + override def configure(): Unit = { + + bind(classOf[Config]).toInstance(configuration.underlying) + + install(new SlickUserModule) + bind(classOf[UserDAOCloseHook]).asEagerSingleton() + } +} + +/** + * Closes database connections safely. Important on dev restart. + */ +class UserDAOCloseHook @Inject() (dao: UserDAO, lifecycle: ApplicationLifecycle) { + private val logger = org.slf4j.LoggerFactory.getLogger("application") + + lifecycle.addStopHook { () => + Future.successful { + logger.info("Now closing database connections!") + dao.close() + } + } +} diff --git a/app/modules/DatabaseModule.scala b/app/modules/DatabaseModule.scala deleted file mode 100644 index de41f9c12..000000000 --- a/app/modules/DatabaseModule.scala +++ /dev/null @@ -1,36 +0,0 @@ -package modules - -import javax.inject.{Inject, Singleton, Provider} - -import com.google.inject.AbstractModule -import com.typesafe.config.Config -import play.api.{Environment, Configuration} -import play.api.inject.ApplicationLifecycle - -import scala.concurrent.Future - -/** - * - */ -class DatabaseModule(environment: Environment, - configuration: Configuration) extends AbstractModule { - override def configure(): Unit = { - bind(classOf[Config]).toInstance(configuration.underlying) - bind(classOf[slick.jdbc.JdbcBackend.Database]).toProvider(classOf[DatabaseProvider]) - bind(classOf[slick.UserDAO]).asEagerSingleton() - } -} - -@Singleton -class DatabaseProvider @Inject() (config: Config, lifecycle: ApplicationLifecycle) extends Provider[slick.jdbc.JdbcBackend.Database] { - - private val db = slick.jdbc.JdbcBackend.Database.forConfig("myapp.database", config) - - lifecycle.addStopHook { () => - Future.successful { - db.close() - } - } - - override def get(): slick.jdbc.JdbcBackend.Database = db -} diff --git a/app/slick/UserDAO.scala b/app/slick/UserDAO.scala deleted file mode 100644 index 76eced0f4..000000000 --- a/app/slick/UserDAO.scala +++ /dev/null @@ -1,54 +0,0 @@ -package slick - -import javax.inject.{Singleton, Inject} - -import akka.actor.ActorSystem -import com.typesafe.config.Config -import models.User - -import scala.concurrent.Future -import slick.driver.PostgresDriver.api._ - -/** - * - */ -@Singleton -class UserDAO @Inject() (config:Config, db:Database) { - - import slick.driver.PostgresDriver.api._ - - private val users = TableQuery[Users] - - private val queryById = Compiled( - (id: Rep[String]) => users.filter(_.id === id)) - - - def lookup(id: String): Future[Option[User]] = { - db.run(queryById(id).result.headOption) - } - - def all: Future[Seq[User]] = { - db.run(users.result) - } - - def update(user:User) = { - db.run(queryById(user.id).update(user)) - } - - def delete(id:String) = { - db.run(queryById(id).delete) - } - - def create(user:User): Future[Int] = { - db.run( - users += user - ) - } - - class Users(tag: Tag) extends Table[User](tag, "USERS") { - def id = column[String]("ID", O.PrimaryKey) - def email = column[String]("EMAIL") - - def * = (id, email) <> (User.tupled, User.unapply) - } -} diff --git a/build.sbt b/build.sbt index 8b8ae049b..bf0fbce65 100644 --- a/build.sbt +++ b/build.sbt @@ -1,18 +1,13 @@ name := """play-slick-3.0""" -version := "1.0-SNAPSHOT" - -lazy val root = (project in file(".")).enablePlugins(PlayScala) - -scalaVersion := "2.11.6" +version := "1.1-SNAPSHOT" libraryDependencies ++= Seq( - "org.postgresql" % "postgresql" % "9.4-1201-jdbc41", - "com.zaxxer" % "HikariCP" % "2.3.8", - "com.typesafe.slick" %% "slick" % "3.0.0", specs2 % Test ) +scalaVersion in ThisBuild := "2.11.7" + resolvers += "scalaz-bintray" at "http://dl.bintray.com/scalaz/releases" resolvers += Resolver.sonatypeRepo("releases") @@ -22,3 +17,22 @@ resolvers += Resolver.sonatypeRepo("snapshots") // Play provides two styles of routers, one expects its actions to be injected, the // other, legacy style, accesses its actions statically. routesGenerator := InjectedRoutesGenerator + +initialize := { + val _ = initialize.value + if (sys.props("java.specification.version") != "1.8") + sys.error("Java 8 is required for this project.") +} + +lazy val root = (project in file(".")) + .enablePlugins(PlayScala) + .aggregate( + api, slick + ).dependsOn(api, slick) + +lazy val api = (project in file("modules/api")) + .enablePlugins(Common) + +lazy val slick = (project in file("modules/slick")) + .enablePlugins(Common) + .dependsOn(api) diff --git a/conf/application.conf b/conf/application.conf index 178b4e660..4bfa5c299 100644 --- a/conf/application.conf +++ b/conf/application.conf @@ -7,25 +7,16 @@ play.crypto.secret = "changeme" # ~~~~~ play.i18n.langs = [ "en" ] -play.modules.enabled += "modules.DatabaseModule" - -myapp = { - database = { - driver = org.postgresql.Driver - url = "jdbc:postgresql://localhost/myapp" - user = "postgres" - password = "postgres" - numThreads = 10 - connectionTimeout = 5000 - validationTimeout = 5000 - } - - dispatcher { - fork-join-executor { - parallelism-factor = 2 - parallelism-max = 20 - } - } +play.modules.enabled += "modules.AppModule" +myapp.database-dispatcher { + type = Dispatcher + executor = "thread-pool-executor" + thread-pool-executor { + // should be same size as connection pool + // see https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing + // http://letitcrash.com/post/40755146949/tuning-dispatchers-in-akka-applications + core-pool-size-min = 10 // minimumIdle + core-pool-size-max = 10 // maximumPoolSize + } } - diff --git a/conf/evolutions/create.sql b/conf/evolutions/create.sql deleted file mode 100644 index 4f10c163d..000000000 --- a/conf/evolutions/create.sql +++ /dev/null @@ -1,3 +0,0 @@ - - -create table "USERS" ("ID" VARCHAR NOT NULL PRIMARY KEY, "EMAIL" VARCHAR(1024)); diff --git a/conf/logback.xml b/conf/logback.xml index 599e40466..7f17a0586 100644 --- a/conf/logback.xml +++ b/conf/logback.xml @@ -1,26 +1,45 @@ - - - - - - %coloredLevel - %logger - %message%n%xException - - - - - - + + + + + ${application.home}/logs/application.log + + %date [%level] from %logger in %thread - %message%n%xException + + + + + + %coloredLevel %logger{15} - %message%n%xException{10} + + + + + + + + + + + + + + + + + + + + - - - + + + + diff --git a/project/Build.scala b/project/Build.scala new file mode 100644 index 000000000..15f985e40 --- /dev/null +++ b/project/Build.scala @@ -0,0 +1,30 @@ +import sbt.{Resolver, AutoPlugin} +import sbt.plugins.JvmPlugin +import sbt.Keys._ +import sbt._ + +object Common extends AutoPlugin { + override def trigger = allRequirements + override def requires = JvmPlugin + + override def projectSettings = Seq( + scalaVersion := "2.11.7", + javacOptions ++= Seq("-source", "1.8", "-target", "1.8"), + scalacOptions ++= Seq( + "-encoding", "UTF-8", // yes, this is 2 args + "-deprecation", + "-feature", + "-unchecked", + "-Xlint", + "-Yno-adapted-args", + "-Ywarn-numeric-widen", + "-Xfatal-warnings" + ), + libraryDependencies ++= Seq( + "javax.inject" % "javax.inject" % "1", + "com.google.inject" % "guice" % "4.0" + ), + scalacOptions in Test ++= Seq("-Yrangepos") + ) +} + diff --git a/project/build.properties b/project/build.properties index a6cd27895..817bc38df 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1,4 +1 @@ -#Activator-generated Properties -#Mon Jun 01 12:58:43 PDT 2015 -template.uuid=4dd0bfb4-5d69-4e44-a1a9-1a18a28af9b4 -sbt.version=0.13.8 +sbt.version=0.13.9 diff --git a/project/plugins.sbt b/project/plugins.sbt index f833ed639..8de722f92 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,5 +1,5 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.4.0") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.4.2") // web plugins @@ -14,3 +14,5 @@ addSbtPlugin("com.typesafe.sbt" % "sbt-rjs" % "1.0.7") addSbtPlugin("com.typesafe.sbt" % "sbt-digest" % "1.1.0") addSbtPlugin("com.typesafe.sbt" % "sbt-mocha" % "1.1.0") + +addSbtPlugin("org.flywaydb" % "flyway-sbt" % "3.2.1") From c637a4972a86654efdabbfc6903457fcaa85ab8d Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Thu, 22 Oct 2015 14:33:39 -0700 Subject: [PATCH 06/53] Add modules directory --- .../main/scala/com/example/user/UserDAO.scala | 23 ++++++++ modules/slick/build.sbt | 21 ++++++++ .../slick/src/main/resources/application.conf | 21 ++++++++ .../db/migration/V1__create_users.sql | 3 ++ .../com/example/user/MyPostgresDriver.scala | 27 ++++++++++ .../scala/com/example/user/SlickUserDAO.scala | 54 +++++++++++++++++++ .../com/example/user/SlickUserModule.scala | 29 ++++++++++ 7 files changed, 178 insertions(+) create mode 100644 modules/api/src/main/scala/com/example/user/UserDAO.scala create mode 100644 modules/slick/build.sbt create mode 100644 modules/slick/src/main/resources/application.conf create mode 100644 modules/slick/src/main/resources/db/migration/V1__create_users.sql create mode 100644 modules/slick/src/main/scala/com/example/user/MyPostgresDriver.scala create mode 100644 modules/slick/src/main/scala/com/example/user/SlickUserDAO.scala create mode 100644 modules/slick/src/main/scala/com/example/user/SlickUserModule.scala diff --git a/modules/api/src/main/scala/com/example/user/UserDAO.scala b/modules/api/src/main/scala/com/example/user/UserDAO.scala new file mode 100644 index 000000000..d73e2c227 --- /dev/null +++ b/modules/api/src/main/scala/com/example/user/UserDAO.scala @@ -0,0 +1,23 @@ +package com.example.user + +import scala.concurrent.Future + +/** + * + */ +trait UserDAO { + + def lookup(id: String): Future[Option[User]] + + def all: Future[Seq[User]] + + def update(user:User) + + def delete(id:String) + + def create(user:User): Future[Int] + + def close() +} + +case class User(id:String, email:String) diff --git a/modules/slick/build.sbt b/modules/slick/build.sbt new file mode 100644 index 000000000..b37e0bff9 --- /dev/null +++ b/modules/slick/build.sbt @@ -0,0 +1,21 @@ + + +libraryDependencies ++= Seq( + "org.postgresql" % "postgresql" % "9.4-1204-jdbc42", + "com.zaxxer" % "HikariCP" % "2.4.1", + "com.typesafe.slick" %% "slick" % "3.1.0", + "com.typesafe.slick" %% "slick-hikaricp" % "3.1.0", + "com.github.tminglei" %% "slick-pg" % "0.10.0", + "com.typesafe.play" %% "play-json" % "2.4.2", // standalone play json + "com.github.tminglei" %% "slick-pg_play-json" % "0.10.0" // play json to postgres json +) + +// Database Migrations: +// run with "sbt flywayMigrate" +// http://flywaydb.org/getstarted/firststeps/sbt.html + +seq(flywaySettings: _*) + +flywayUrl := "jdbc:postgresql://localhost:5432/myapp" + +flywayUser := "myapp" diff --git a/modules/slick/src/main/resources/application.conf b/modules/slick/src/main/resources/application.conf new file mode 100644 index 000000000..beb1f8b96 --- /dev/null +++ b/modules/slick/src/main/resources/application.conf @@ -0,0 +1,21 @@ + +myapp = { + database = { + driver = org.postgresql.Driver + url = "jdbc:postgresql://localhost:5432/myapp" + user = "myapp" + password = "changeme" + password = "${DB_PASSWORD}" // use the environment variable + numThreads = 10 + connectionTimeout = 5000 + validationTimeout = 5000 + } + + dispatcher { + fork-join-executor { + parallelism-factor = 2 + parallelism-max = 20 + } + } +} + diff --git a/modules/slick/src/main/resources/db/migration/V1__create_users.sql b/modules/slick/src/main/resources/db/migration/V1__create_users.sql new file mode 100644 index 000000000..4f10c163d --- /dev/null +++ b/modules/slick/src/main/resources/db/migration/V1__create_users.sql @@ -0,0 +1,3 @@ + + +create table "USERS" ("ID" VARCHAR NOT NULL PRIMARY KEY, "EMAIL" VARCHAR(1024)); diff --git a/modules/slick/src/main/scala/com/example/user/MyPostgresDriver.scala b/modules/slick/src/main/scala/com/example/user/MyPostgresDriver.scala new file mode 100644 index 000000000..8e4a35597 --- /dev/null +++ b/modules/slick/src/main/scala/com/example/user/MyPostgresDriver.scala @@ -0,0 +1,27 @@ +package com.example.user + +import com.github.tminglei.slickpg._ +import play.api.libs.json.{Json, JsValue} + +trait MyPostgresDriver extends ExPostgresDriver +with PgArraySupport +with PgDateSupport +with PgPlayJsonSupport { + + object MyAPI extends API with DateTimeImplicits with JsonImplicits { + implicit val strListTypeMapper = new SimpleArrayJdbcType[String]("text").to(_.toList) + implicit val playJsonArrayTypeMapper = + new AdvancedArrayJdbcType[JsValue](pgjson, + (s) => utils.SimpleArrayUtils.fromString[JsValue](Json.parse(_))(s).orNull, + (v) => utils.SimpleArrayUtils.mkString[JsValue](_.toString())(v) + ).to(_.toList) + } + + // jsonb support is in postgres 9.4.0 onward; for 9.3.x use "json" + def pgjson = "jsonb" + + override val api = MyAPI +} + +object MyPostgresDriver extends MyPostgresDriver + diff --git a/modules/slick/src/main/scala/com/example/user/SlickUserDAO.scala b/modules/slick/src/main/scala/com/example/user/SlickUserDAO.scala new file mode 100644 index 000000000..a4bbeed0a --- /dev/null +++ b/modules/slick/src/main/scala/com/example/user/SlickUserDAO.scala @@ -0,0 +1,54 @@ +package com.example.user + +import javax.inject.{Inject, Singleton} + +import scala.concurrent.Future + +import slick.jdbc.JdbcBackend.Database + +/** + * + */ +@Singleton +class SlickUserDAO @Inject() (db:Database) extends UserDAO { + import MyPostgresDriver.api._ + + private val users = TableQuery[Users] + + private val queryById = Compiled( + (id: Rep[String]) => users.filter(_.id === id)) + + + def lookup(id: String): Future[Option[User]] = { + db.run(queryById(id).result.headOption) + } + + def all: Future[Seq[User]] = { + db.run(users.result) + } + + def update(user:User) = { + db.run(queryById(user.id).update(user)) + } + + def delete(id:String) = { + db.run(queryById(id).delete) + } + + def create(user:User): Future[Int] = { + db.run( + users += user + ) + } + + def close(): Unit = { + db.close() + } + + class Users(tag: Tag) extends Table[User](tag, "USERS") { + def id = column[String]("ID", O.PrimaryKey) + def email = column[String]("EMAIL") + + def * = (id, email) <> (User.tupled, User.unapply) + } +} diff --git a/modules/slick/src/main/scala/com/example/user/SlickUserModule.scala b/modules/slick/src/main/scala/com/example/user/SlickUserModule.scala new file mode 100644 index 000000000..bf8906128 --- /dev/null +++ b/modules/slick/src/main/scala/com/example/user/SlickUserModule.scala @@ -0,0 +1,29 @@ +package com.example.user + +import javax.inject._ +import com.google.inject.AbstractModule +import com.typesafe.config.Config + +import scala.concurrent.Future + +/** + * + */ +class SlickUserModule extends AbstractModule { + override def configure(): Unit = { + bind(classOf[slick.jdbc.JdbcBackend.Database]).toProvider(classOf[DatabaseProvider]) + bind(classOf[UserDAO]).to(classOf[SlickUserDAO]) + } +} + + +@Singleton +class DatabaseProvider @Inject() (config: Config) extends Provider[slick.jdbc.JdbcBackend.Database] { + + private val db = slick.jdbc.JdbcBackend.Database.forConfig("myapp.database", config) + + override def get(): slick.jdbc.JdbcBackend.Database = db +} + + + From 09baa3b7dacd3bd70f883946d44f494909f00a79 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Fri, 11 Mar 2016 22:05:53 -0800 Subject: [PATCH 07/53] Upgrade to Play 2.5 Add a custom execution context Update project to use code generation correctly --- .gitignore | 3 +- README.md | 105 ++++++++++++++++-- app/controllers/Application.scala | 24 ---- app/modules/AppModule.scala | 37 ------ app/views/index.scala.html | 5 - build.sbt | 28 ++--- .../main/scala/com/example/user/UserDAO.scala | 28 +++-- modules/flyway/build.sbt | 21 ++++ .../R__AddCurrentUpdatedAtTimestamp.scala | 24 ++++ .../V20150409112518__create_users_table.sql | 6 + .../migration/V20150409131208__add_user.sql | 5 + modules/play/app/Module.scala | 60 ++++++++++ .../play/app/controllers/HomeController.scala | 25 +++++ modules/play/app/views/index.scala.html | 22 ++++ .../play/app}/views/main.scala.html | 0 modules/play/build.sbt | 3 + {conf => modules/play/conf}/application.conf | 2 - {conf => modules/play/conf}/logback.xml | 2 +- {conf => modules/play/conf}/routes | 2 +- modules/play/project/plugins.sbt | 14 +++ .../play/public}/images/favicon.png | Bin .../play/public}/javascripts/hello.js | 0 .../play/public}/stylesheets/main.css | 0 modules/slick/build.sbt | 49 +++++--- .../slick/src/main/resources/application.conf | 5 +- .../db/migration/V1__create_users.sql | 3 - .../scala/com/example/user/SlickUserDAO.scala | 54 --------- .../com/example/user/SlickUserModule.scala | 29 ----- .../user/{ => slick}/MyPostgresDriver.scala | 9 +- .../com/example/user/slick/SlickUserDAO.scala | 65 +++++++++++ project/Build.scala | 11 +- project/build.properties | 2 +- project/plugins.sbt | 23 ++-- test/ApplicationSpec.scala | 30 ----- test/IntegrationSpec.scala | 24 ---- 35 files changed, 435 insertions(+), 285 deletions(-) delete mode 100644 app/controllers/Application.scala delete mode 100644 app/modules/AppModule.scala delete mode 100644 app/views/index.scala.html create mode 100644 modules/flyway/build.sbt create mode 100644 modules/flyway/src/main/java/db/migration/R__AddCurrentUpdatedAtTimestamp.scala create mode 100644 modules/flyway/src/main/resources/db/migration/V20150409112518__create_users_table.sql create mode 100644 modules/flyway/src/main/resources/db/migration/V20150409131208__add_user.sql create mode 100644 modules/play/app/Module.scala create mode 100644 modules/play/app/controllers/HomeController.scala create mode 100644 modules/play/app/views/index.scala.html rename {app => modules/play/app}/views/main.scala.html (100%) create mode 100644 modules/play/build.sbt rename {conf => modules/play/conf}/application.conf (92%) rename {conf => modules/play/conf}/logback.xml (97%) rename {conf => modules/play/conf}/routes (79%) create mode 100644 modules/play/project/plugins.sbt rename {public => modules/play/public}/images/favicon.png (100%) rename {public => modules/play/public}/javascripts/hello.js (100%) rename {public => modules/play/public}/stylesheets/main.css (100%) delete mode 100644 modules/slick/src/main/resources/db/migration/V1__create_users.sql delete mode 100644 modules/slick/src/main/scala/com/example/user/SlickUserDAO.scala delete mode 100644 modules/slick/src/main/scala/com/example/user/SlickUserModule.scala rename modules/slick/src/main/scala/com/example/user/{ => slick}/MyPostgresDriver.scala (81%) create mode 100644 modules/slick/src/main/scala/com/example/user/slick/SlickUserDAO.scala delete mode 100644 test/ApplicationSpec.scala delete mode 100644 test/IntegrationSpec.scala diff --git a/.gitignore b/.gitignore index ec080a7ba..fe2b15530 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ /.idea +modules/play/logs/ + # sbt specific .cache .history @@ -22,7 +24,6 @@ bin/ .eclipse /lib/ /logs/ -/modules /project/project /project/target /target diff --git a/README.md b/README.md index 58ad2d82d..c8cd30e1a 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,50 @@ -## Play 2.4 with Slick 3.1 +# Play 2.5 with Slick 3.1 -This is an example project that shows Play 2.4 working with a self contained Slick 3.1.0 module. It uses HikariCP with PostgreSQL 9.4. +This project shows Play 2.5 working with Slick. +This project is configured to keep all the modules self-contained. + +* Slick is isolated from Play, not using play-slick. +* Database migration is done using [Flyway](), not Play Evolutions. +* Slick's classes are auto-generated following database migration. + +## Database + +The sample application is configured to use PostgreSQL and has some custom drivers to make Postgres / Slick integration easier. + +If you are using PostgreSQL for the first time, follow the instructions to [install on Mac using HomeBrew](http://exponential.io/blog/2015/02/21/install-postgresql-on-mac-os-x-via-brew/), and then start up PostgreSQL. ``` -sudo su - postgres -createdb -U postgres myapp +postgres -D /usr/local/var/postgres ``` -Then check that "modules/slick/build.sbt" has the URL that you want, and type: +``` +sudo su - postgres # if on Linux +``` + +The default Postgres user and password for this application is "myuser/mypass", db is "myapp": ``` -sbt flywayMigrate +createuser myuser +createdb myapp ``` -This creates the tables for the Slick module. Please see the [flyways documentation](http://flywaydb.org/getstarted/firststeps/sbt.html) for more details. +### Database Migration +Start up `sbt` and go into the flyway module to run database migrations: +``` +project flyway +flywayMigrate +``` -Note that the Slick module is self-contained and does not have any Play references. Play JSON can be used standalone. +Please see the [flyways documentation](http://flywaydb.org/getstarted/firststeps/sbt.html) for more details. + +## User DAO The module is defined with a public API in "modules/api": -``` +```scala package com.example.user trait UserDAO { @@ -38,18 +60,77 @@ trait UserDAO { def create(user:User): Future[Int] } + +case class User(id:String, email:String) + +trait UserDAOExecutionContext extends ExecutionContext ``` -and the main Play application uses the exposed API without touching the database implementation, using Guice DI. +Play works with the DAO by installing the `SlickUserModule` and assigning a custom execution context: -There is a drawback to this model: when used in development mode, +``` +class Module(environment: Environment, + configuration: Configuration) extends AbstractModule { + override def configure(): Unit = { + bind(classOf[Config]).toInstance(configuration.underlying) + bind(classOf[UserDAOExecutionContext]).toProvider(classOf[SlickUserDAOExecutionContextProvider]) + + install(new SlickUserModule) + bind(classOf[UserDAOCloseHook]).asEagerSingleton() + } +} ``` -class DatabaseProvider @Inject() (dao: UserDAO, lifecycle: ApplicationLifecycle) extends Provider[] { + +The DAO must be closed to release JDBC connections, and this is handled through `UserDAOCloseHook`: + +```scala +class UserDAOCloseHook @Inject() (dao: UserDAO, lifecycle: ApplicationLifecycle) { + private val logger = org.slf4j.LoggerFactory.getLogger("application") + lifecycle.addStopHook { () => Future.successful { + logger.info("Now closing database connections!") dao.close() } } } ``` + +## Slick + +Slick configuration is simple. The `User` case class is mapped to a `Users` table and the queries are implemented + +``` +@Singleton +class SlickUserDAO @Inject()(db: Database) extends UserDAO { + + import MyPostgresDriver.api._ + + private val users: TableQuery[Users] = TableQuery[Users] + + // ... + + class Users(tag: Tag) extends Table[User](tag, "users") { + def id = column[String]("id", O.PrimaryKey) + + def email = column[String]("email") + + def * = (id, email) <>(User.tupled, User.unapply) + } +} +``` + +Slick [schema code generation](http://slick.typesafe.com/doc/3.1.0/code-generation.html) tool, which will create the `Tables` object under `target/scala-2.11/src_managed`. + + + +## Running + +To run the project, start up Play: + +``` +project play +run +``` + diff --git a/app/controllers/Application.scala b/app/controllers/Application.scala deleted file mode 100644 index 7b9ebc0a0..000000000 --- a/app/controllers/Application.scala +++ /dev/null @@ -1,24 +0,0 @@ -package controllers - -import java.util.UUID -import javax.inject.Inject - -import akka.actor.ActorSystem -import com.example.user.{User, UserDAO} -import play.api._ -import play.api.mvc._ - -class Application @Inject() (userDAO: UserDAO, actorSystem:ActorSystem) extends Controller { - - private val ec = actorSystem.dispatchers.lookup("myapp.database-dispatcher") - - def index = Action.async { - // Set up an execution context from the akka dispatchers library... - - val id = UUID.randomUUID.toString - userDAO.create(User(id, "some@example.com")).map { rows => - Ok(views.html.index(s"Connected to database and created user $id")) - }(ec) - } - -} diff --git a/app/modules/AppModule.scala b/app/modules/AppModule.scala deleted file mode 100644 index 547182e3b..000000000 --- a/app/modules/AppModule.scala +++ /dev/null @@ -1,37 +0,0 @@ -package modules - -import javax.inject.Inject - -import com.example.user.{UserDAO, SlickUserModule} -import com.google.inject.AbstractModule -import com.typesafe.config.Config -import play.api.inject.ApplicationLifecycle - -import play.api.{Environment, Configuration} - -import scala.concurrent.Future - -class AppModule(environment: Environment, - configuration: Configuration) extends AbstractModule { - override def configure(): Unit = { - - bind(classOf[Config]).toInstance(configuration.underlying) - - install(new SlickUserModule) - bind(classOf[UserDAOCloseHook]).asEagerSingleton() - } -} - -/** - * Closes database connections safely. Important on dev restart. - */ -class UserDAOCloseHook @Inject() (dao: UserDAO, lifecycle: ApplicationLifecycle) { - private val logger = org.slf4j.LoggerFactory.getLogger("application") - - lifecycle.addStopHook { () => - Future.successful { - logger.info("Now closing database connections!") - dao.close() - } - } -} diff --git a/app/views/index.scala.html b/app/views/index.scala.html deleted file mode 100644 index 71a170107..000000000 --- a/app/views/index.scala.html +++ /dev/null @@ -1,5 +0,0 @@ -@(message: String) - -
- Hello world! @message -
\ No newline at end of file diff --git a/build.sbt b/build.sbt index bf0fbce65..a5cb3507d 100644 --- a/build.sbt +++ b/build.sbt @@ -2,37 +2,27 @@ name := """play-slick-3.0""" version := "1.1-SNAPSHOT" -libraryDependencies ++= Seq( - specs2 % Test -) - scalaVersion in ThisBuild := "2.11.7" -resolvers += "scalaz-bintray" at "http://dl.bintray.com/scalaz/releases" - -resolvers += Resolver.sonatypeRepo("releases") - -resolvers += Resolver.sonatypeRepo("snapshots") - -// Play provides two styles of routers, one expects its actions to be injected, the -// other, legacy style, accesses its actions statically. -routesGenerator := InjectedRoutesGenerator - initialize := { val _ = initialize.value if (sys.props("java.specification.version") != "1.8") sys.error("Java 8 is required for this project.") } -lazy val root = (project in file(".")) - .enablePlugins(PlayScala) - .aggregate( - api, slick - ).dependsOn(api, slick) +lazy val flyway = (project in file("modules/flyway")) + .enablePlugins(FlywayPlugin) lazy val api = (project in file("modules/api")) .enablePlugins(Common) lazy val slick = (project in file("modules/slick")) .enablePlugins(Common) + .aggregate(api) .dependsOn(api) + +lazy val play = (project in file("modules/play")) + .enablePlugins(PlayScala) + .aggregate(api, slick) + .dependsOn(api, slick) + diff --git a/modules/api/src/main/scala/com/example/user/UserDAO.scala b/modules/api/src/main/scala/com/example/user/UserDAO.scala index d73e2c227..484b03f59 100644 --- a/modules/api/src/main/scala/com/example/user/UserDAO.scala +++ b/modules/api/src/main/scala/com/example/user/UserDAO.scala @@ -1,23 +1,35 @@ package com.example.user -import scala.concurrent.Future +import java.util.UUID + +import org.joda.time.{DateTime, Instant} + +import scala.concurrent.{ExecutionContext, Future} /** - * + * An implementation dependent DAO. This could be implemented by Slick, Cassandra, or a REST API. */ trait UserDAO { - def lookup(id: String): Future[Option[User]] + def lookup(id: UUID)(implicit ec: UserDAOExecutionContext): Future[Option[User]] - def all: Future[Seq[User]] + def all(implicit ec: UserDAOExecutionContext): Future[Seq[User]] - def update(user:User) + def update(user: User)(implicit ec: UserDAOExecutionContext): Future[Int] - def delete(id:String) + def delete(id: UUID)(implicit ec: UserDAOExecutionContext): Future[Int] - def create(user:User): Future[Int] + def create(user: User)(implicit ec: UserDAOExecutionContext): Future[Int] def close() } -case class User(id:String, email:String) +/** + * Implementation independent aggregate root. + */ +case class User(id: UUID, email: String, createdAt: DateTime, updatedAt: Option[DateTime]) + +/** + * Type safe execution context for operations on UserDAO. + */ +trait UserDAOExecutionContext extends ExecutionContext diff --git a/modules/flyway/build.sbt b/modules/flyway/build.sbt new file mode 100644 index 000000000..a397aa44f --- /dev/null +++ b/modules/flyway/build.sbt @@ -0,0 +1,21 @@ + + +// Database Migrations: +// run with "sbt flywayMigrate" +// http://flywaydb.org/getstarted/firststeps/sbt.html + +//$ export DB_DEFAULT_URL="jdbc:h2:/tmp/example.db" +//$ export DB_DEFAULT_USER="sa" +//$ export DB_DEFAULT_PASSWORD="" + +libraryDependencies += "org.flywaydb" % "flyway-core" % "4.0" + +lazy val databaseUrl = sys.env.getOrElse("DB_DEFAULT_URL", "jdbc:postgresql://localhost:5432/myapp") +lazy val databaseUser = sys.env.getOrElse("DB_DEFAULT_USER", "myuser") +lazy val databasePassword = sys.env.getOrElse("DB_DEFAULT_PASSWORD", "mypass") + +flywayLocations := Seq("classpath:db/migration") + +flywayUrl := databaseUrl +flywayUser := databaseUser +flywayPassword := databasePassword diff --git a/modules/flyway/src/main/java/db/migration/R__AddCurrentUpdatedAtTimestamp.scala b/modules/flyway/src/main/java/db/migration/R__AddCurrentUpdatedAtTimestamp.scala new file mode 100644 index 000000000..ec46e0736 --- /dev/null +++ b/modules/flyway/src/main/java/db/migration/R__AddCurrentUpdatedAtTimestamp.scala @@ -0,0 +1,24 @@ +package db.migration + +import java.sql.{Connection, Timestamp} + +import org.flywaydb.core.api.migration.jdbc.JdbcMigration + +/** + * Adds a repeatable Flyway migration in Scala. + * + * See Java migrations + * for more details. + */ +class R__AddCurrentUpdatedAtTimestamp extends JdbcMigration { + + override def migrate(c: Connection): Unit = { + val statement = c.prepareStatement("UPDATE users SET updated_at = ?") + try { + statement.setTimestamp(1, new Timestamp(System.currentTimeMillis())) + statement.execute() + } finally { + statement.close() + } + } +} diff --git a/modules/flyway/src/main/resources/db/migration/V20150409112518__create_users_table.sql b/modules/flyway/src/main/resources/db/migration/V20150409112518__create_users_table.sql new file mode 100644 index 000000000..fe522b159 --- /dev/null +++ b/modules/flyway/src/main/resources/db/migration/V20150409112518__create_users_table.sql @@ -0,0 +1,6 @@ +create table "users" ( + "id" UUID PRIMARY KEY NOT NULL, + "email" VARCHAR(1024) NOT NULL, + created_at TIMESTAMPTZ NOT NULL, + updated_at TIMESTAMPTZ NULL +); diff --git a/modules/flyway/src/main/resources/db/migration/V20150409131208__add_user.sql b/modules/flyway/src/main/resources/db/migration/V20150409131208__add_user.sql new file mode 100644 index 000000000..8979d51ab --- /dev/null +++ b/modules/flyway/src/main/resources/db/migration/V20150409131208__add_user.sql @@ -0,0 +1,5 @@ +INSERT INTO "users" VALUES ( + 'd074bce8-a8ca-49ec-9225-a50ffe83dc2f', + 'myuser@gmail.com', + (TIMESTAMPTZ '2013-03-26T17:50:06Z') + ); diff --git a/modules/play/app/Module.scala b/modules/play/app/Module.scala new file mode 100644 index 000000000..80f5781a3 --- /dev/null +++ b/modules/play/app/Module.scala @@ -0,0 +1,60 @@ +import javax.inject.{Inject, Provider, Singleton} + +import com.example.user.slick.SlickUserDAO +import com.example.user.{UserDAO, UserDAOExecutionContext} +import com.google.inject.AbstractModule +import com.typesafe.config.Config +import play.api.inject.ApplicationLifecycle +import play.api.{Configuration, Environment} + +import scala.concurrent.{ExecutionContext, Future} + +class Module(environment: Environment, + configuration: Configuration) extends AbstractModule { + override def configure(): Unit = { + + bind(classOf[Config]).toInstance(configuration.underlying) + bind(classOf[UserDAOExecutionContext]).toProvider(classOf[SlickUserDAOExecutionContextProvider]) + + bind(classOf[slick.jdbc.JdbcBackend.Database]).toProvider(classOf[DatabaseProvider]) + bind(classOf[UserDAO]).to(classOf[SlickUserDAO]) + + bind(classOf[UserDAOCloseHook]).asEagerSingleton() + } +} + +@Singleton +class DatabaseProvider @Inject() (config: Config) extends Provider[slick.jdbc.JdbcBackend.Database] { + + private val db = slick.jdbc.JdbcBackend.Database.forConfig("myapp.database", config) + + override def get(): slick.jdbc.JdbcBackend.Database = db +} + +@Singleton +class SlickUserDAOExecutionContextProvider @Inject() (actorSystem: akka.actor.ActorSystem) extends Provider[UserDAOExecutionContext] { + private val instance = { + val ec = actorSystem.dispatchers.lookup("myapp.database-dispatcher") + new SlickUserDAOExecutionContext(ec) + } + + override def get() = instance +} + +class SlickUserDAOExecutionContext(ec: ExecutionContext) extends UserDAOExecutionContext { + override def execute(runnable: Runnable): Unit = ec.execute(runnable) + + override def reportFailure(cause: Throwable): Unit = ec.reportFailure(cause) +} + +/** Closes database connections safely. Important on dev restart. */ +class UserDAOCloseHook @Inject()(dao: UserDAO, lifecycle: ApplicationLifecycle) { + private val logger = org.slf4j.LoggerFactory.getLogger("application") + + lifecycle.addStopHook { () => + Future.successful { + logger.info("Now closing database connections!") + dao.close() + } + } +} diff --git a/modules/play/app/controllers/HomeController.scala b/modules/play/app/controllers/HomeController.scala new file mode 100644 index 000000000..b086c80a7 --- /dev/null +++ b/modules/play/app/controllers/HomeController.scala @@ -0,0 +1,25 @@ +package controllers + +import java.util.UUID +import javax.inject.{Inject, Singleton} + +import akka.actor.ActorSystem +import com.example.user.{User, UserDAO, UserDAOExecutionContext} +import play.api.mvc._ + +@Singleton +class HomeController @Inject() (userDAO: UserDAO, userDAOExecutionContext: UserDAOExecutionContext) extends Controller { + + private val logger = org.slf4j.LoggerFactory.getLogger(this.getClass) + + implicit val ec = userDAOExecutionContext + + def index = Action.async { + logger.info("Calling index") + userDAO.all.map { users => + logger.info(s"Calling index: users = ${users}") + Ok(views.html.index(users)) + } + } + +} diff --git a/modules/play/app/views/index.scala.html b/modules/play/app/views/index.scala.html new file mode 100644 index 000000000..6a5dbe123 --- /dev/null +++ b/modules/play/app/views/index.scala.html @@ -0,0 +1,22 @@ +@(users: Seq[User]) + +@main("Title Page") { +

Users

+ + + + + + + + + @for(user <- users){ + + + + + + + } +
IdEmailCreated AtUpdated At
@user.id@user.email@user.createdAt@user.updatedAt
+} diff --git a/app/views/main.scala.html b/modules/play/app/views/main.scala.html similarity index 100% rename from app/views/main.scala.html rename to modules/play/app/views/main.scala.html diff --git a/modules/play/build.sbt b/modules/play/build.sbt new file mode 100644 index 000000000..b9c826dde --- /dev/null +++ b/modules/play/build.sbt @@ -0,0 +1,3 @@ + +// Adding this means no explicit import in *.scala.html files +TwirlKeys.templateImports += "com.example.user.User" diff --git a/conf/application.conf b/modules/play/conf/application.conf similarity index 92% rename from conf/application.conf rename to modules/play/conf/application.conf index 4bfa5c299..eae76bfaf 100644 --- a/conf/application.conf +++ b/modules/play/conf/application.conf @@ -7,8 +7,6 @@ play.crypto.secret = "changeme" # ~~~~~ play.i18n.langs = [ "en" ] -play.modules.enabled += "modules.AppModule" - myapp.database-dispatcher { type = Dispatcher executor = "thread-pool-executor" diff --git a/conf/logback.xml b/modules/play/conf/logback.xml similarity index 97% rename from conf/logback.xml rename to modules/play/conf/logback.xml index 7f17a0586..3f18878ff 100644 --- a/conf/logback.xml +++ b/modules/play/conf/logback.xml @@ -1,6 +1,6 @@ - + ${application.home}/logs/application.log diff --git a/conf/routes b/modules/play/conf/routes similarity index 79% rename from conf/routes rename to modules/play/conf/routes index e71a0bd06..f59f0a3b1 100644 --- a/conf/routes +++ b/modules/play/conf/routes @@ -3,7 +3,7 @@ # ~~~~ # Home page -GET / controllers.Application.index +GET / controllers.HomeController.index # Map static resources from the /public folder to the /assets URL path GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset) diff --git a/modules/play/project/plugins.sbt b/modules/play/project/plugins.sbt new file mode 100644 index 000000000..3229200a9 --- /dev/null +++ b/modules/play/project/plugins.sbt @@ -0,0 +1,14 @@ +// web plugins + +addSbtPlugin("com.typesafe.sbt" % "sbt-coffeescript" % "1.0.0") + +addSbtPlugin("com.typesafe.sbt" % "sbt-less" % "1.0.6") + +addSbtPlugin("com.typesafe.sbt" % "sbt-jshint" % "1.0.3") + +addSbtPlugin("com.typesafe.sbt" % "sbt-rjs" % "1.0.7") + +addSbtPlugin("com.typesafe.sbt" % "sbt-digest" % "1.1.0") + +addSbtPlugin("com.typesafe.sbt" % "sbt-mocha" % "1.1.0") + diff --git a/public/images/favicon.png b/modules/play/public/images/favicon.png similarity index 100% rename from public/images/favicon.png rename to modules/play/public/images/favicon.png diff --git a/public/javascripts/hello.js b/modules/play/public/javascripts/hello.js similarity index 100% rename from public/javascripts/hello.js rename to modules/play/public/javascripts/hello.js diff --git a/public/stylesheets/main.css b/modules/play/public/stylesheets/main.css similarity index 100% rename from public/stylesheets/main.css rename to modules/play/public/stylesheets/main.css diff --git a/modules/slick/build.sbt b/modules/slick/build.sbt index b37e0bff9..19a38eb08 100644 --- a/modules/slick/build.sbt +++ b/modules/slick/build.sbt @@ -1,21 +1,44 @@ - +import slick.codegen.SourceCodeGenerator +import slick.{ model => m } libraryDependencies ++= Seq( - "org.postgresql" % "postgresql" % "9.4-1204-jdbc42", "com.zaxxer" % "HikariCP" % "2.4.1", - "com.typesafe.slick" %% "slick" % "3.1.0", - "com.typesafe.slick" %% "slick-hikaricp" % "3.1.0", - "com.github.tminglei" %% "slick-pg" % "0.10.0", - "com.typesafe.play" %% "play-json" % "2.4.2", // standalone play json - "com.github.tminglei" %% "slick-pg_play-json" % "0.10.0" // play json to postgres json + "com.typesafe.slick" %% "slick" % "3.1.1", + "com.typesafe.slick" %% "slick-hikaricp" % "3.1.1", + "org.postgresql" % "postgresql" % "9.4-1201-jdbc41", + "com.github.tminglei" %% "slick-pg" % "0.12.0", + "com.github.tminglei" %% "slick-pg_play-json" % "0.12.0", + "com.github.tminglei" %% "slick-pg_joda-time" % "0.12.0", + "com.github.tototoshi" %% "slick-joda-mapper" % "2.1.0" ) -// Database Migrations: -// run with "sbt flywayMigrate" -// http://flywaydb.org/getstarted/firststeps/sbt.html +lazy val databaseUrl = sys.env.getOrElse("DB_DEFAULT_URL", "jdbc:postgresql:myapp") +lazy val databaseUser = sys.env.getOrElse("DB_DEFAULT_USER", "myuser") +lazy val databasePassword = sys.env.getOrElse("DB_DEFAULT_PASSWORD", "mypass") -seq(flywaySettings: _*) +slickCodegenSettings +slickCodegenDatabaseUrl := databaseUrl +slickCodegenDatabaseUser := databaseUser +slickCodegenDatabasePassword := databasePassword +slickCodegenDriver := slick.driver.PostgresDriver +slickCodegenJdbcDriver := "org.postgresql.Driver" +slickCodegenOutputPackage := "com.example.user.slick" +slickCodegenExcludedTables := Seq("schema_version") -flywayUrl := "jdbc:postgresql://localhost:5432/myapp" +slickCodegenCodeGenerator := { (model: m.Model) => + new SourceCodeGenerator(model) { + override def code = + "import com.github.tototoshi.slick.H2JodaSupport._\n" + "import org.joda.time.DateTime\n" + super.code + override def Table = new Table(_) { + override def Column = new Column(_) { + override def rawType = model.tpe match { + case "java.sql.Timestamp" => "DateTime" // kill j.s.Timestamp + case _ => + super.rawType + } + } + } + } +} -flywayUser := "myapp" +sourceGenerators in Compile <+= slickCodegen diff --git a/modules/slick/src/main/resources/application.conf b/modules/slick/src/main/resources/application.conf index beb1f8b96..1d86b65fe 100644 --- a/modules/slick/src/main/resources/application.conf +++ b/modules/slick/src/main/resources/application.conf @@ -3,9 +3,8 @@ myapp = { database = { driver = org.postgresql.Driver url = "jdbc:postgresql://localhost:5432/myapp" - user = "myapp" - password = "changeme" - password = "${DB_PASSWORD}" // use the environment variable + user = "myuser" + password = "mypass" numThreads = 10 connectionTimeout = 5000 validationTimeout = 5000 diff --git a/modules/slick/src/main/resources/db/migration/V1__create_users.sql b/modules/slick/src/main/resources/db/migration/V1__create_users.sql deleted file mode 100644 index 4f10c163d..000000000 --- a/modules/slick/src/main/resources/db/migration/V1__create_users.sql +++ /dev/null @@ -1,3 +0,0 @@ - - -create table "USERS" ("ID" VARCHAR NOT NULL PRIMARY KEY, "EMAIL" VARCHAR(1024)); diff --git a/modules/slick/src/main/scala/com/example/user/SlickUserDAO.scala b/modules/slick/src/main/scala/com/example/user/SlickUserDAO.scala deleted file mode 100644 index a4bbeed0a..000000000 --- a/modules/slick/src/main/scala/com/example/user/SlickUserDAO.scala +++ /dev/null @@ -1,54 +0,0 @@ -package com.example.user - -import javax.inject.{Inject, Singleton} - -import scala.concurrent.Future - -import slick.jdbc.JdbcBackend.Database - -/** - * - */ -@Singleton -class SlickUserDAO @Inject() (db:Database) extends UserDAO { - import MyPostgresDriver.api._ - - private val users = TableQuery[Users] - - private val queryById = Compiled( - (id: Rep[String]) => users.filter(_.id === id)) - - - def lookup(id: String): Future[Option[User]] = { - db.run(queryById(id).result.headOption) - } - - def all: Future[Seq[User]] = { - db.run(users.result) - } - - def update(user:User) = { - db.run(queryById(user.id).update(user)) - } - - def delete(id:String) = { - db.run(queryById(id).delete) - } - - def create(user:User): Future[Int] = { - db.run( - users += user - ) - } - - def close(): Unit = { - db.close() - } - - class Users(tag: Tag) extends Table[User](tag, "USERS") { - def id = column[String]("ID", O.PrimaryKey) - def email = column[String]("EMAIL") - - def * = (id, email) <> (User.tupled, User.unapply) - } -} diff --git a/modules/slick/src/main/scala/com/example/user/SlickUserModule.scala b/modules/slick/src/main/scala/com/example/user/SlickUserModule.scala deleted file mode 100644 index bf8906128..000000000 --- a/modules/slick/src/main/scala/com/example/user/SlickUserModule.scala +++ /dev/null @@ -1,29 +0,0 @@ -package com.example.user - -import javax.inject._ -import com.google.inject.AbstractModule -import com.typesafe.config.Config - -import scala.concurrent.Future - -/** - * - */ -class SlickUserModule extends AbstractModule { - override def configure(): Unit = { - bind(classOf[slick.jdbc.JdbcBackend.Database]).toProvider(classOf[DatabaseProvider]) - bind(classOf[UserDAO]).to(classOf[SlickUserDAO]) - } -} - - -@Singleton -class DatabaseProvider @Inject() (config: Config) extends Provider[slick.jdbc.JdbcBackend.Database] { - - private val db = slick.jdbc.JdbcBackend.Database.forConfig("myapp.database", config) - - override def get(): slick.jdbc.JdbcBackend.Database = db -} - - - diff --git a/modules/slick/src/main/scala/com/example/user/MyPostgresDriver.scala b/modules/slick/src/main/scala/com/example/user/slick/MyPostgresDriver.scala similarity index 81% rename from modules/slick/src/main/scala/com/example/user/MyPostgresDriver.scala rename to modules/slick/src/main/scala/com/example/user/slick/MyPostgresDriver.scala index 8e4a35597..b22be7d10 100644 --- a/modules/slick/src/main/scala/com/example/user/MyPostgresDriver.scala +++ b/modules/slick/src/main/scala/com/example/user/slick/MyPostgresDriver.scala @@ -1,11 +1,14 @@ -package com.example.user +package com.example.user.slick import com.github.tminglei.slickpg._ -import play.api.libs.json.{Json, JsValue} +import play.api.libs.json.{JsValue, Json} +/** + * A postgresql driver with extended Joda and JSON support. + */ trait MyPostgresDriver extends ExPostgresDriver with PgArraySupport -with PgDateSupport +with PgDateSupportJoda with PgPlayJsonSupport { object MyAPI extends API with DateTimeImplicits with JsonImplicits { diff --git a/modules/slick/src/main/scala/com/example/user/slick/SlickUserDAO.scala b/modules/slick/src/main/scala/com/example/user/slick/SlickUserDAO.scala new file mode 100644 index 000000000..554521248 --- /dev/null +++ b/modules/slick/src/main/scala/com/example/user/slick/SlickUserDAO.scala @@ -0,0 +1,65 @@ +package com.example.user.slick + +import java.util.UUID +import javax.inject.{Inject, Singleton} + +import org.joda.time.DateTime +import slick.driver.JdbcProfile +import slick.jdbc.JdbcBackend.Database +import com.example.user._ + +import scala.concurrent.Future +import scala.language.implicitConversions + +/** + * A User DAO implemented with Slick, leveraging Slick code gen. + * + * Note that you must run "flyway/flywayMigrate" before "compile" here. + */ +@Singleton +class SlickUserDAO @Inject()(db: Database) extends UserDAO with Tables { + + import MyPostgresDriver.api._ + + private val queryById = Compiled( + (id: Rep[UUID]) => Users.filter(_.id === id)) + + def lookup(id: UUID)(implicit ec: UserDAOExecutionContext): Future[Option[User]] = { + val f: Future[Option[UsersRow]] = db.run(queryById(id).result.headOption) + f.map(maybeRow => maybeRow.map(usersRowToUser(_))) + } + + def all(implicit ec: UserDAOExecutionContext): Future[Seq[User]] = { + val f = db.run(Users.result) + f.map(seq => seq.map(usersRowToUser(_))) + } + + def update(user: User)(implicit ec: UserDAOExecutionContext): Future[Int] = { + db.run(queryById(user.id).update(userToUsersRow(user))) + } + + def delete(id: UUID)(implicit ec: UserDAOExecutionContext): Future[Int] = { + db.run(queryById(id).delete) + } + + def create(user: User)(implicit ec: UserDAOExecutionContext): Future[Int] = { + db.run( + Users += userToUsersRow(user.copy(createdAt = DateTime.now())) + ) + } + + def close(): Unit = { + db.close() + } + + private def userToUsersRow(user:User): UsersRow = { + UsersRow(user.id, user.email, user.createdAt, user.updatedAt) + } + + private def usersRowToUser(usersRow:UsersRow): User = { + User(usersRow.id, usersRow.email, usersRow.createdAt, usersRow.updatedAt) + } + + // Use the custom postgresql driver. + override val profile: JdbcProfile = MyPostgresDriver +} diff --git a/project/Build.scala b/project/Build.scala index 15f985e40..78dc9f464 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1,7 +1,7 @@ -import sbt.{Resolver, AutoPlugin} +import sbt.{AutoPlugin, Resolver} import sbt.plugins.JvmPlugin import sbt.Keys._ -import sbt._ +import sbt.{Resolver, _} object Common extends AutoPlugin { override def trigger = allRequirements @@ -20,11 +20,16 @@ object Common extends AutoPlugin { "-Ywarn-numeric-widen", "-Xfatal-warnings" ), + resolvers ++= Seq( + "scalaz-bintray" at "http://dl.bintray.com/scalaz/releases", + Resolver.sonatypeRepo("releases"), + Resolver.sonatypeRepo("snapshots")), libraryDependencies ++= Seq( "javax.inject" % "javax.inject" % "1", + "joda-time" % "joda-time" % "2.9.2", + "org.joda" % "joda-convert" % "1.2", "com.google.inject" % "guice" % "4.0" ), scalacOptions in Test ++= Seq("-Yrangepos") ) } - diff --git a/project/build.properties b/project/build.properties index 817bc38df..43b8278c6 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=0.13.9 +sbt.version=0.13.11 diff --git a/project/plugins.sbt b/project/plugins.sbt index 8de722f92..1500e0c5c 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,18 +1,17 @@ -// The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.4.2") - -// web plugins +resolvers += "Typesafe repository" at "https://repo.typesafe.com/typesafe/releases/" -addSbtPlugin("com.typesafe.sbt" % "sbt-coffeescript" % "1.0.0") +resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots" -addSbtPlugin("com.typesafe.sbt" % "sbt-less" % "1.0.6") +resolvers += "Flyway" at "https://flywaydb.org/repo" -addSbtPlugin("com.typesafe.sbt" % "sbt-jshint" % "1.0.3") +// Database migration +addSbtPlugin("org.flywaydb" % "flyway-sbt" % "4.0") -addSbtPlugin("com.typesafe.sbt" % "sbt-rjs" % "1.0.7") +// Slick code generation +// https://github.com/tototoshi/sbt-slick-codegen +addSbtPlugin("com.github.tototoshi" % "sbt-slick-codegen" % "1.2.0") -addSbtPlugin("com.typesafe.sbt" % "sbt-digest" % "1.1.0") +libraryDependencies += "org.postgresql" % "postgresql" % "9.4-1201-jdbc41" -addSbtPlugin("com.typesafe.sbt" % "sbt-mocha" % "1.1.0") - -addSbtPlugin("org.flywaydb" % "flyway-sbt" % "3.2.1") +// The Play plugin +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.5.0") diff --git a/test/ApplicationSpec.scala b/test/ApplicationSpec.scala deleted file mode 100644 index 036a94e87..000000000 --- a/test/ApplicationSpec.scala +++ /dev/null @@ -1,30 +0,0 @@ -import org.specs2.mutable._ -import org.specs2.runner._ -import org.junit.runner._ - -import play.api.test._ -import play.api.test.Helpers._ - -/** - * Add your spec here. - * You can mock out a whole application including requests, plugins etc. - * For more information, consult the wiki. - */ -@RunWith(classOf[JUnitRunner]) -class ApplicationSpec extends Specification { - - "Application" should { - - "send 404 on a bad request" in new WithApplication{ - route(FakeRequest(GET, "/boum")) must beSome.which (status(_) == NOT_FOUND) - } - - "render the index page" in new WithApplication{ - val home = route(FakeRequest(GET, "/")).get - - status(home) must equalTo(OK) - contentType(home) must beSome.which(_ == "text/html") - contentAsString(home) must contain ("Your new application is ready.") - } - } -} diff --git a/test/IntegrationSpec.scala b/test/IntegrationSpec.scala deleted file mode 100644 index 652edde7f..000000000 --- a/test/IntegrationSpec.scala +++ /dev/null @@ -1,24 +0,0 @@ -import org.specs2.mutable._ -import org.specs2.runner._ -import org.junit.runner._ - -import play.api.test._ -import play.api.test.Helpers._ - -/** - * add your integration spec here. - * An integration test will fire up a whole play application in a real (or headless) browser - */ -@RunWith(classOf[JUnitRunner]) -class IntegrationSpec extends Specification { - - "Application" should { - - "work from within a browser" in new WithBrowser { - - browser.goTo("http://localhost:" + port) - - browser.pageSource must contain("Your new application is ready.") - } - } -} From c4223f62ae5e10876ae80fdabdb442758e461501 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Sun, 13 Mar 2016 14:47:35 -0700 Subject: [PATCH 08/53] Add updated readme --- README.md | 126 ++++++++++++++---- .../com/example/user/slick/SlickUserDAO.scala | 12 +- 2 files changed, 109 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index c8cd30e1a..4c5cea7ac 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,8 @@ sudo su - postgres # if on Linux The default Postgres user and password for this application is "myuser/mypass", db is "myapp": ``` -createuser myuser createdb myapp +createuser --pwprompt myuser ``` ### Database Migration @@ -66,9 +66,65 @@ case class User(id:String, email:String) trait UserDAOExecutionContext extends ExecutionContext ``` -Play works with the DAO by installing the `SlickUserModule` and assigning a custom execution context: +## Slick + +Slick configuration is simple, because the Slick [schema code generation](http://slick.typesafe.com/doc/3.1.0/code-generation.html) will look at the tables created from Flyway, and automatically generate a `Tables` trait. From there, `UsersRow` and `Users` are created automatically. Some conversion code is necessary to map between `UsersRow` and `User`. + +```scala +@Singleton +class SlickUserDAO @Inject()(db: Database) extends UserDAO with Tables { + + // Use the custom postgresql driver. + override val profile: JdbcProfile = MyPostgresDriver + + import profile.api._ + + private val queryById = Compiled( + (id: Rep[UUID]) => Users.filter(_.id === id)) + + def lookup(id: UUID)(implicit ec: UserDAOExecutionContext): Future[Option[User]] = { + val f: Future[Option[UsersRow]] = db.run(queryById(id).result.headOption) + f.map(maybeRow => maybeRow.map(usersRowToUser(_))) + } + + def all(implicit ec: UserDAOExecutionContext): Future[Seq[User]] = { + val f = db.run(Users.result) + f.map(seq => seq.map(usersRowToUser(_))) + } + + def update(user: User)(implicit ec: UserDAOExecutionContext): Future[Int] = { + db.run(queryById(user.id).update(userToUsersRow(user))) + } + def delete(id: UUID)(implicit ec: UserDAOExecutionContext): Future[Int] = { + db.run(queryById(id).delete) + } + + def create(user: User)(implicit ec: UserDAOExecutionContext): Future[Int] = { + db.run( + Users += userToUsersRow(user.copy(createdAt = DateTime.now())) + ) + } + + def close(): Unit = { + db.close() + } + + private def userToUsersRow(user:User): UsersRow = { + UsersRow(user.id, user.email, user.createdAt, user.updatedAt) + } + + private def usersRowToUser(usersRow:UsersRow): User = { + User(usersRow.id, usersRow.email, usersRow.createdAt, usersRow.updatedAt) + } +} ``` + +## Play + +The root `Module.scala` file contains all the classes need to bind Slick and expose it as a `UserDAO`: + +```scala class Module(environment: Environment, configuration: Configuration) extends AbstractModule { override def configure(): Unit = { @@ -76,16 +132,47 @@ class Module(environment: Environment, bind(classOf[Config]).toInstance(configuration.underlying) bind(classOf[UserDAOExecutionContext]).toProvider(classOf[SlickUserDAOExecutionContextProvider]) - install(new SlickUserModule) + bind(classOf[slick.jdbc.JdbcBackend.Database]).toProvider(classOf[DatabaseProvider]) + bind(classOf[UserDAO]).to(classOf[SlickUserDAO]) + bind(classOf[UserDAOCloseHook]).asEagerSingleton() } } ``` +There are a couple of providers to do a "lazy get" of the database and execution context from configuration: + +``` +@Singleton +class DatabaseProvider @Inject() (config: Config) extends Provider[slick.jdbc.JdbcBackend.Database] { + + private val db = slick.jdbc.JdbcBackend.Database.forConfig("myapp.database", config) + + override def get(): slick.jdbc.JdbcBackend.Database = db +} + +@Singleton +class SlickUserDAOExecutionContextProvider @Inject() (actorSystem: akka.actor.ActorSystem) extends Provider[UserDAOExecutionContext] { + private val instance = { + val ec = actorSystem.dispatchers.lookup("myapp.database-dispatcher") + new SlickUserDAOExecutionContext(ec) + } + + override def get() = instance +} + +class SlickUserDAOExecutionContext(ec: ExecutionContext) extends UserDAOExecutionContext { + override def execute(runnable: Runnable): Unit = ec.execute(runnable) + + override def reportFailure(cause: Throwable): Unit = ec.reportFailure(cause) +} +``` + The DAO must be closed to release JDBC connections, and this is handled through `UserDAOCloseHook`: ```scala -class UserDAOCloseHook @Inject() (dao: UserDAO, lifecycle: ApplicationLifecycle) { +/** Closes database connections safely. Important on dev restart. */ +class UserDAOCloseHook @Inject()(dao: UserDAO, lifecycle: ApplicationLifecycle) { private val logger = org.slf4j.LoggerFactory.getLogger("application") lifecycle.addStopHook { () => @@ -97,34 +184,26 @@ class UserDAOCloseHook @Inject() (dao: UserDAO, lifecycle: ApplicationLifecycle) } ``` -## Slick +From there, the controller code is simple: -Slick configuration is simple. The `User` case class is mapped to a `Users` table and the queries are implemented - -``` +```scala @Singleton -class SlickUserDAO @Inject()(db: Database) extends UserDAO { +class HomeController @Inject() (userDAO: UserDAO, userDAOExecutionContext: UserDAOExecutionContext) extends Controller { - import MyPostgresDriver.api._ + private val logger = org.slf4j.LoggerFactory.getLogger(this.getClass) - private val users: TableQuery[Users] = TableQuery[Users] + implicit val ec = userDAOExecutionContext - // ... - - class Users(tag: Tag) extends Table[User](tag, "users") { - def id = column[String]("id", O.PrimaryKey) - - def email = column[String]("email") - - def * = (id, email) <>(User.tupled, User.unapply) + def index = Action.async { + logger.info("Calling index") + userDAO.all.map { users => + logger.info(s"Calling index: users = ${users}") + Ok(views.html.index(users)) + } } } ``` -Slick [schema code generation](http://slick.typesafe.com/doc/3.1.0/code-generation.html) tool, which will create the `Tables` object under `target/scala-2.11/src_managed`. - - - ## Running To run the project, start up Play: @@ -134,3 +213,4 @@ project play run ``` +And that's it! diff --git a/modules/slick/src/main/scala/com/example/user/slick/SlickUserDAO.scala b/modules/slick/src/main/scala/com/example/user/slick/SlickUserDAO.scala index 554521248..90cfba187 100644 --- a/modules/slick/src/main/scala/com/example/user/slick/SlickUserDAO.scala +++ b/modules/slick/src/main/scala/com/example/user/slick/SlickUserDAO.scala @@ -19,7 +19,10 @@ import scala.language.implicitConversions @Singleton class SlickUserDAO @Inject()(db: Database) extends UserDAO with Tables { - import MyPostgresDriver.api._ + // Use the custom postgresql driver. + override val profile: JdbcProfile = MyPostgresDriver + + import profile.api._ private val queryById = Compiled( (id: Rep[UUID]) => Users.filter(_.id === id)) @@ -52,14 +55,11 @@ class SlickUserDAO @Inject()(db: Database) extends UserDAO with Tables { db.close() } - private def userToUsersRow(user:User): UsersRow = { + private def userToUsersRow(user: User): UsersRow = { UsersRow(user.id, user.email, user.createdAt, user.updatedAt) } - private def usersRowToUser(usersRow:UsersRow): User = { + private def usersRowToUser(usersRow: UsersRow): User = { User(usersRow.id, usersRow.email, usersRow.createdAt, usersRow.updatedAt) } - - // Use the custom postgresql driver. - override val profile: JdbcProfile = MyPostgresDriver } From f7c0c1523eb33c958483d2207034e280b6d38034 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Sun, 13 Mar 2016 16:33:29 -0700 Subject: [PATCH 09/53] More documentation, use Future[Unit] for close() --- README.md | 91 ++++++++++++++++--- .../main/scala/com/example/user/UserDAO.scala | 5 +- .../migration/V20150409131208__add_user.sql | 2 +- .../R__AddCurrentUpdatedAtTimestamp.scala | 0 .../com/example/user/slick/SlickUserDAO.scala | 4 +- 5 files changed, 86 insertions(+), 16 deletions(-) rename modules/flyway/src/main/{java => scala}/db/migration/R__AddCurrentUpdatedAtTimestamp.scala (100%) diff --git a/README.md b/README.md index 4c5cea7ac..52e83efc2 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,46 @@ createuser --pwprompt myuser ### Database Migration +The first thing to do is to run the database scripts. Flyways has a number of advantages over Play Evolutions: it allows for both Java migrations and SQL migrations, and has command line support. + +There are two migration scripts that will be run, `create_users` which adds a "users" table. + +``` +create table "users" ( + "id" UUID PRIMARY KEY NOT NULL, + "email" VARCHAR(1024) NOT NULL, + created_at TIMESTAMPTZ NOT NULL, + updated_at TIMESTAMPTZ NULL +); +``` + +and `add_user` which inserts a single user into the table: + +``` +INSERT INTO "users" VALUES ( + 'd074bce8-a8ca-49ec-9225-a50ffe83dc2f', + 'myuser@example.com', + (TIMESTAMPTZ '2013-03-26T17:50:06Z') + ); +``` + +Flyway also allows repeatable migrations that can be Java or Scala based: + +```scala +class R__AddCurrentUpdatedAtTimestamp extends JdbcMigration { + + override def migrate(c: Connection): Unit = { + val statement = c.prepareStatement("UPDATE users SET updated_at = ?") + try { + statement.setTimestamp(1, new Timestamp(System.currentTimeMillis())) + statement.execute() + } finally { + statement.close() + } + } +} +``` + Start up `sbt` and go into the flyway module to run database migrations: ``` @@ -38,36 +78,61 @@ project flyway flywayMigrate ``` -Please see the [flyways documentation](http://flywaydb.org/getstarted/firststeps/sbt.html) for more details. +See [the flyways documentation](http://flywaydb.org/getstarted/firststeps/sbt.html) for more details. ## User DAO -The module is defined with a public API in "modules/api": +The module is defined with a public API in "modules/api" -- everything returns a Future, and there are custom [Joda Time](http://www.joda.org/joda-time/) `DateTime` objects which aren't mapped through Slick by default. ```scala -package com.example.user - trait UserDAO { - def lookup(id: String): Future[Option[User]] + def lookup(id: UUID)(implicit ec: UserDAOExecutionContext): Future[Option[User]] - def all: Future[Seq[User]] + def all(implicit ec: UserDAOExecutionContext): Future[Seq[User]] - def update(user:User) + def update(user: User)(implicit ec: UserDAOExecutionContext): Future[Int] - def delete(id:String) + def delete(id: UUID)(implicit ec: UserDAOExecutionContext): Future[Int] - def create(user:User): Future[Int] + def create(user: User)(implicit ec: UserDAOExecutionContext): Future[Int] + def close(): Future[Unit] } -case class User(id:String, email:String) +case class User(id: UUID, email: String, createdAt: DateTime, updatedAt: Option[DateTime]) trait UserDAOExecutionContext extends ExecutionContext ``` ## Slick +The Postgres Driver for Slick is configured with [slick-pg](https://github.com/tminglei/slick-pg), which allows for custom mapping between PostgreSQL data types and Joda Time data types: + +```scala +trait MyPostgresDriver extends ExPostgresDriver +with PgArraySupport +with PgDateSupportJoda +with PgPlayJsonSupport { + + object MyAPI extends API with DateTimeImplicits with JsonImplicits { + implicit val strListTypeMapper = new SimpleArrayJdbcType[String]("text").to(_.toList) + implicit val playJsonArrayTypeMapper = + new AdvancedArrayJdbcType[JsValue](pgjson, + (s) => utils.SimpleArrayUtils.fromString[JsValue](Json.parse(_))(s).orNull, + (v) => utils.SimpleArrayUtils.mkString[JsValue](_.toString())(v) + ).to(_.toList) + } + + // jsonb support is in postgres 9.4.0 onward; for 9.3.x use "json" + def pgjson = "jsonb" + + override val api = MyAPI +} + +object MyPostgresDriver extends MyPostgresDriver +``` + Slick configuration is simple, because the Slick [schema code generation](http://slick.typesafe.com/doc/3.1.0/code-generation.html) will look at the tables created from Flyway, and automatically generate a `Tables` trait. From there, `UsersRow` and `Users` are created automatically. Some conversion code is necessary to map between `UsersRow` and `User`. ```scala @@ -106,8 +171,8 @@ class SlickUserDAO @Inject()(db: Database) extends UserDAO with Tables { ) } - def close(): Unit = { - db.close() + def close(): Future[Unit] = { + Future.successful(db.close()) } private def userToUsersRow(user:User): UsersRow = { @@ -120,6 +185,8 @@ class SlickUserDAO @Inject()(db: Database) extends UserDAO with Tables { } ``` +Once `SlickUserDAO` is compiled, everything is available to be bound and run in the Play application. + ## Play The root `Module.scala` file contains all the classes need to bind Slick and expose it as a `UserDAO`: diff --git a/modules/api/src/main/scala/com/example/user/UserDAO.scala b/modules/api/src/main/scala/com/example/user/UserDAO.scala index 484b03f59..a9f44b3f3 100644 --- a/modules/api/src/main/scala/com/example/user/UserDAO.scala +++ b/modules/api/src/main/scala/com/example/user/UserDAO.scala @@ -21,11 +21,14 @@ trait UserDAO { def create(user: User)(implicit ec: UserDAOExecutionContext): Future[Int] - def close() + def close(): Future[Unit] } /** * Implementation independent aggregate root. + * + * Note that this uses Joda Time classes and UUID, which are specifically mapped + * through the custom postgres driver. */ case class User(id: UUID, email: String, createdAt: DateTime, updatedAt: Option[DateTime]) diff --git a/modules/flyway/src/main/resources/db/migration/V20150409131208__add_user.sql b/modules/flyway/src/main/resources/db/migration/V20150409131208__add_user.sql index 8979d51ab..17ef1fe36 100644 --- a/modules/flyway/src/main/resources/db/migration/V20150409131208__add_user.sql +++ b/modules/flyway/src/main/resources/db/migration/V20150409131208__add_user.sql @@ -1,5 +1,5 @@ INSERT INTO "users" VALUES ( 'd074bce8-a8ca-49ec-9225-a50ffe83dc2f', - 'myuser@gmail.com', + 'myuser@example.com', (TIMESTAMPTZ '2013-03-26T17:50:06Z') ); diff --git a/modules/flyway/src/main/java/db/migration/R__AddCurrentUpdatedAtTimestamp.scala b/modules/flyway/src/main/scala/db/migration/R__AddCurrentUpdatedAtTimestamp.scala similarity index 100% rename from modules/flyway/src/main/java/db/migration/R__AddCurrentUpdatedAtTimestamp.scala rename to modules/flyway/src/main/scala/db/migration/R__AddCurrentUpdatedAtTimestamp.scala diff --git a/modules/slick/src/main/scala/com/example/user/slick/SlickUserDAO.scala b/modules/slick/src/main/scala/com/example/user/slick/SlickUserDAO.scala index 90cfba187..dbb92f766 100644 --- a/modules/slick/src/main/scala/com/example/user/slick/SlickUserDAO.scala +++ b/modules/slick/src/main/scala/com/example/user/slick/SlickUserDAO.scala @@ -51,8 +51,8 @@ class SlickUserDAO @Inject()(db: Database) extends UserDAO with Tables { ) } - def close(): Unit = { - db.close() + def close(): Future[Unit] = { + Future.successful(db.close()) } private def userToUsersRow(user: User): UsersRow = { From bbcfff71a58bd2464745d67d550a1d0b3d55251f Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Sun, 13 Mar 2016 16:37:50 -0700 Subject: [PATCH 10/53] More docs --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 52e83efc2..3c6c5c37c 100644 --- a/README.md +++ b/README.md @@ -209,7 +209,7 @@ class Module(environment: Environment, There are a couple of providers to do a "lazy get" of the database and execution context from configuration: -``` +```scala @Singleton class DatabaseProvider @Inject() (config: Config) extends Provider[slick.jdbc.JdbcBackend.Database] { @@ -238,7 +238,6 @@ class SlickUserDAOExecutionContext(ec: ExecutionContext) extends UserDAOExecutio The DAO must be closed to release JDBC connections, and this is handled through `UserDAOCloseHook`: ```scala -/** Closes database connections safely. Important on dev restart. */ class UserDAOCloseHook @Inject()(dao: UserDAO, lifecycle: ApplicationLifecycle) { private val logger = org.slf4j.LoggerFactory.getLogger("application") @@ -280,4 +279,4 @@ project play run ``` -And that's it! +And that's it! Now when you go to http://localhost:9000, you will see the list of users in the database. From 4ee0992a340a2e8e28e7b36c418d71b7be1c6ea1 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Sun, 13 Mar 2016 16:40:01 -0700 Subject: [PATCH 11/53] Fix link --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3c6c5c37c..8b0396453 100644 --- a/README.md +++ b/README.md @@ -279,4 +279,6 @@ project play run ``` -And that's it! Now when you go to http://localhost:9000, you will see the list of users in the database. +And that's it! + +Now go to [http://localhost:9000](http://localhost:9000), and you will see the list of users in the database. From 16f71f2326564d89cac397bf5d3e7a48941e910b Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Fri, 24 Feb 2017 17:29:41 -0800 Subject: [PATCH 12/53] Refactor project for Play 2.6.x --- .travis.yml | 15 ++++++++++++++ README.md | 13 ++++++++---- {modules/play/app => app}/Module.scala | 2 -- .../controllers/HomeController.scala | 6 ++++-- .../play/app => app}/views/index.scala.html | 0 .../play/app => app}/views/main.scala.html | 0 build.sbt | 19 +++++++++++------- {modules/play/conf => conf}/application.conf | 9 --------- {modules/play/conf => conf}/logback.xml | 0 {modules/play/conf => conf}/routes | 0 modules/play/build.sbt | 3 --- modules/play/project/plugins.sbt | 14 ------------- project/Build.scala | 10 +++------ project/build.properties | 2 +- project/plugins.sbt | 2 +- .../play/public => public}/images/favicon.png | Bin .../public => public}/javascripts/hello.js | 0 .../public => public}/stylesheets/main.css | 0 18 files changed, 45 insertions(+), 50 deletions(-) create mode 100644 .travis.yml rename {modules/play/app => app}/Module.scala (96%) rename {modules/play/app => app}/controllers/HomeController.scala (64%) rename {modules/play/app => app}/views/index.scala.html (100%) rename {modules/play/app => app}/views/main.scala.html (100%) rename {modules/play/conf => conf}/application.conf (71%) rename {modules/play/conf => conf}/logback.xml (100%) rename {modules/play/conf => conf}/routes (100%) delete mode 100644 modules/play/build.sbt delete mode 100644 modules/play/project/plugins.sbt rename {modules/play/public => public}/images/favicon.png (100%) rename {modules/play/public => public}/javascripts/hello.js (100%) rename {modules/play/public => public}/stylesheets/main.css (100%) diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..0c5572296 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,15 @@ +language: scala +scala: +- 2.12.1 +jdk: +- oraclejdk8 +cache: + directories: + - "$HOME/.ivy2/cache" +before_cache: +- rm -rf $HOME/.ivy2/cache/com.typesafe.play/* +- rm -rf $HOME/.ivy2/cache/scala_*/sbt_*/com.typesafe.play/* +- find $HOME/.ivy2/cache -name "ivydata-*.properties" -print0 | xargs -n10 -0 rm +notifications: + slack: + secure: bHNB8qvP8Da51PgA8tHiM4ePhusZlLIFXNItkyo0HuS1L1I06BRKOe2tFyU6dy8hz8cIEh2lFbQ5WIhjXFXPxqez6SdacFtInX4BRj7Q6QJP7Q+79h3OHQAz6niYWaGZZ1BtMucSlZcXYiAky5Tsj3cOesUM18T/HLrSfg8UjJHflSyT3wWnYcuRIHuC/YPjNqUn7Brrf/yaRClR80ffgx7eq/KF912bupsj/17eF/rPs65B2ux6M0yeSdUiXLIDNSm9Dh18ZTb+Hq4RjryT9EySO4X7rsuauqPWbA2OM0EFT3+0QLxD+y8EEAZGqGvEYnoEfikuIqCCr5v5Zsayg3J7mb5hgpqTZQWazmJFQ/+m76vP5YOP8/xI2+gbCv95kYHmkSItHza/VZhwP06pQyVMs2kXVoejwRm27M+WG36r+2bjU23o7R/EmyLMjcOdvXF/+Czi3KzMCDyX8AxVl9VOilvGlvTitwkmba1GM2pST9Pr81zh6ktyBsfx3f/DstalgkaVB+ftFrxRsawwlxKy6Xg8hSIR/ifsWdIhTucqMwjvUa7PmCtDZcIiXTgQVBBTVHtHmzuM07zo/0jTaQ/sP0Hlj4YgHz87/1lwoTTEVErLcVyl1FG30vrt8rophMk/cl8UGmw17f5XRh9jVfGwokh5UuUS1/4RcUvBqlM= diff --git a/README.md b/README.md index 8b0396453..db1f73bda 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Play 2.5 with Slick 3.1 +# Play with Slick 3.1 -This project shows Play 2.5 working with Slick. +This project shows Play working with Slick. This project is configured to keep all the modules self-contained. @@ -187,6 +187,12 @@ class SlickUserDAO @Inject()(db: Database) extends UserDAO with Tables { Once `SlickUserDAO` is compiled, everything is available to be bound and run in the Play application. +To run table generation changes manually: + +```scala +genChanges +``` + ## Play The root `Module.scala` file contains all the classes need to bind Slick and expose it as a `UserDAO`: @@ -275,8 +281,7 @@ class HomeController @Inject() (userDAO: UserDAO, userDAOExecutionContext: UserD To run the project, start up Play: ``` -project play -run +sbt run ``` And that's it! diff --git a/modules/play/app/Module.scala b/app/Module.scala similarity index 96% rename from modules/play/app/Module.scala rename to app/Module.scala index 80f5781a3..ee3ac696c 100644 --- a/modules/play/app/Module.scala +++ b/app/Module.scala @@ -12,8 +12,6 @@ import scala.concurrent.{ExecutionContext, Future} class Module(environment: Environment, configuration: Configuration) extends AbstractModule { override def configure(): Unit = { - - bind(classOf[Config]).toInstance(configuration.underlying) bind(classOf[UserDAOExecutionContext]).toProvider(classOf[SlickUserDAOExecutionContextProvider]) bind(classOf[slick.jdbc.JdbcBackend.Database]).toProvider(classOf[DatabaseProvider]) diff --git a/modules/play/app/controllers/HomeController.scala b/app/controllers/HomeController.scala similarity index 64% rename from modules/play/app/controllers/HomeController.scala rename to app/controllers/HomeController.scala index b086c80a7..6990c0565 100644 --- a/modules/play/app/controllers/HomeController.scala +++ b/app/controllers/HomeController.scala @@ -8,13 +8,15 @@ import com.example.user.{User, UserDAO, UserDAOExecutionContext} import play.api.mvc._ @Singleton -class HomeController @Inject() (userDAO: UserDAO, userDAOExecutionContext: UserDAOExecutionContext) extends Controller { +class HomeController @Inject() (userDAO: UserDAO, + userDAOExecutionContext: UserDAOExecutionContext, + cc: ControllerComponents) extends AbstractController(cc) { private val logger = org.slf4j.LoggerFactory.getLogger(this.getClass) implicit val ec = userDAOExecutionContext - def index = Action.async { + def index = Action.async { implicit request => logger.info("Calling index") userDAO.all.map { users => logger.info(s"Calling index: users = ${users}") diff --git a/modules/play/app/views/index.scala.html b/app/views/index.scala.html similarity index 100% rename from modules/play/app/views/index.scala.html rename to app/views/index.scala.html diff --git a/modules/play/app/views/main.scala.html b/app/views/main.scala.html similarity index 100% rename from modules/play/app/views/main.scala.html rename to app/views/main.scala.html diff --git a/build.sbt b/build.sbt index a5cb3507d..022998c01 100644 --- a/build.sbt +++ b/build.sbt @@ -1,8 +1,9 @@ -name := """play-slick-3.0""" + +name := """play-isolated-slick""" version := "1.1-SNAPSHOT" -scalaVersion in ThisBuild := "2.11.7" +scalaVersion in ThisBuild := "2.11.8" initialize := { val _ = initialize.value @@ -14,15 +15,19 @@ lazy val flyway = (project in file("modules/flyway")) .enablePlugins(FlywayPlugin) lazy val api = (project in file("modules/api")) - .enablePlugins(Common) + .settings(Common.projectSettings) lazy val slick = (project in file("modules/slick")) - .enablePlugins(Common) + .settings(Common.projectSettings) .aggregate(api) .dependsOn(api) -lazy val play = (project in file("modules/play")) +lazy val root = (project in file(".")) .enablePlugins(PlayScala) - .aggregate(api, slick) - .dependsOn(api, slick) + .settings( + libraryDependencies += guice, + // Adding this means no explicit import in *.scala.html files + TwirlKeys.templateImports += "com.example.user.User" + ).aggregate(api, slick) + .dependsOn(api, slick, flyway) diff --git a/modules/play/conf/application.conf b/conf/application.conf similarity index 71% rename from modules/play/conf/application.conf rename to conf/application.conf index eae76bfaf..c3ff64bae 100644 --- a/modules/play/conf/application.conf +++ b/conf/application.conf @@ -1,12 +1,3 @@ -# This is the main configuration file for the application. -# ~~~~~ - -play.crypto.secret = "changeme" - -# The application languages -# ~~~~~ -play.i18n.langs = [ "en" ] - myapp.database-dispatcher { type = Dispatcher executor = "thread-pool-executor" diff --git a/modules/play/conf/logback.xml b/conf/logback.xml similarity index 100% rename from modules/play/conf/logback.xml rename to conf/logback.xml diff --git a/modules/play/conf/routes b/conf/routes similarity index 100% rename from modules/play/conf/routes rename to conf/routes diff --git a/modules/play/build.sbt b/modules/play/build.sbt deleted file mode 100644 index b9c826dde..000000000 --- a/modules/play/build.sbt +++ /dev/null @@ -1,3 +0,0 @@ - -// Adding this means no explicit import in *.scala.html files -TwirlKeys.templateImports += "com.example.user.User" diff --git a/modules/play/project/plugins.sbt b/modules/play/project/plugins.sbt deleted file mode 100644 index 3229200a9..000000000 --- a/modules/play/project/plugins.sbt +++ /dev/null @@ -1,14 +0,0 @@ -// web plugins - -addSbtPlugin("com.typesafe.sbt" % "sbt-coffeescript" % "1.0.0") - -addSbtPlugin("com.typesafe.sbt" % "sbt-less" % "1.0.6") - -addSbtPlugin("com.typesafe.sbt" % "sbt-jshint" % "1.0.3") - -addSbtPlugin("com.typesafe.sbt" % "sbt-rjs" % "1.0.7") - -addSbtPlugin("com.typesafe.sbt" % "sbt-digest" % "1.1.0") - -addSbtPlugin("com.typesafe.sbt" % "sbt-mocha" % "1.1.0") - diff --git a/project/Build.scala b/project/Build.scala index 78dc9f464..67e94e8a9 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1,14 +1,10 @@ -import sbt.{AutoPlugin, Resolver} -import sbt.plugins.JvmPlugin import sbt.Keys._ import sbt.{Resolver, _} -object Common extends AutoPlugin { - override def trigger = allRequirements - override def requires = JvmPlugin +object Common { - override def projectSettings = Seq( - scalaVersion := "2.11.7", + def projectSettings = Seq( + scalaVersion := "2.11.8", javacOptions ++= Seq("-source", "1.8", "-target", "1.8"), scalacOptions ++= Seq( "-encoding", "UTF-8", // yes, this is 2 args diff --git a/project/build.properties b/project/build.properties index 43b8278c6..5f32afe7d 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=0.13.11 +sbt.version=0.13.13 \ No newline at end of file diff --git a/project/plugins.sbt b/project/plugins.sbt index 1500e0c5c..a45e1b594 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -14,4 +14,4 @@ addSbtPlugin("com.github.tototoshi" % "sbt-slick-codegen" % "1.2.0") libraryDependencies += "org.postgresql" % "postgresql" % "9.4-1201-jdbc41" // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.5.0") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-M1") diff --git a/modules/play/public/images/favicon.png b/public/images/favicon.png similarity index 100% rename from modules/play/public/images/favicon.png rename to public/images/favicon.png diff --git a/modules/play/public/javascripts/hello.js b/public/javascripts/hello.js similarity index 100% rename from modules/play/public/javascripts/hello.js rename to public/javascripts/hello.js diff --git a/modules/play/public/stylesheets/main.css b/public/stylesheets/main.css similarity index 100% rename from modules/play/public/stylesheets/main.css rename to public/stylesheets/main.css From b3fff5bee86e2008ef00cdfd8d34b4ffb0fa0d71 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Sun, 26 Feb 2017 16:00:57 -0800 Subject: [PATCH 13/53] Move to h2, add functional test --- .gitignore | 1 + .travis.yml | 2 +- README.md | 267 +----------------- app/Module.scala | 50 ++-- app/controllers/HomeController.scala | 18 +- build.sbt | 23 +- conf/application.conf | 11 - conf/logback.xml | 62 ++-- .../main/scala/com/example/user/UserDAO.scala | 19 +- modules/flyway/build.sbt | 6 +- .../V20150409112518__create_users_table.sql | 6 +- .../migration/V20150409131208__add_user.sql | 2 +- .../R__AddCurrentUpdatedAtTimestamp.scala | 24 -- modules/slick/build.sbt | 16 +- .../slick/src/main/resources/application.conf | 39 ++- .../example/user/slick/MyPostgresDriver.scala | 30 -- .../com/example/user/slick/SlickUserDAO.scala | 22 +- project/Build.scala | 2 +- project/plugins.sbt | 2 +- test/controller/FunctionalSpec.scala | 21 ++ test/controller/MyApplicationFactory.scala | 42 +++ 21 files changed, 193 insertions(+), 472 deletions(-) delete mode 100644 conf/application.conf delete mode 100644 modules/flyway/src/main/scala/db/migration/R__AddCurrentUpdatedAtTimestamp.scala delete mode 100644 modules/slick/src/main/scala/com/example/user/slick/MyPostgresDriver.scala create mode 100644 test/controller/FunctionalSpec.scala create mode 100644 test/controller/MyApplicationFactory.scala diff --git a/.gitignore b/.gitignore index fe2b15530..4c15fc12d 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,4 @@ server.pid /dist/ +test.mv.db diff --git a/.travis.yml b/.travis.yml index 0c5572296..5eb091632 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: scala scala: -- 2.12.1 +- 2.11.8 jdk: - oraclejdk8 cache: diff --git a/README.md b/README.md index db1f73bda..09c8473ad 100644 --- a/README.md +++ b/README.md @@ -2,280 +2,21 @@ This project shows Play working with Slick. -This project is configured to keep all the modules self-contained. +This project is configured to keep all the modules self-contained. * Slick is isolated from Play, not using play-slick. * Database migration is done using [Flyway](), not Play Evolutions. * Slick's classes are auto-generated following database migration. -## Database - -The sample application is configured to use PostgreSQL and has some custom drivers to make Postgres / Slick integration easier. - -If you are using PostgreSQL for the first time, follow the instructions to [install on Mac using HomeBrew](http://exponential.io/blog/2015/02/21/install-postgresql-on-mac-os-x-via-brew/), and then start up PostgreSQL. - -``` -postgres -D /usr/local/var/postgres -``` - -``` -sudo su - postgres # if on Linux -``` - -The default Postgres user and password for this application is "myuser/mypass", db is "myapp": - -``` -createdb myapp -createuser --pwprompt myuser -``` - -### Database Migration - -The first thing to do is to run the database scripts. Flyways has a number of advantages over Play Evolutions: it allows for both Java migrations and SQL migrations, and has command line support. - -There are two migration scripts that will be run, `create_users` which adds a "users" table. - -``` -create table "users" ( - "id" UUID PRIMARY KEY NOT NULL, - "email" VARCHAR(1024) NOT NULL, - created_at TIMESTAMPTZ NOT NULL, - updated_at TIMESTAMPTZ NULL -); -``` - -and `add_user` which inserts a single user into the table: - -``` -INSERT INTO "users" VALUES ( - 'd074bce8-a8ca-49ec-9225-a50ffe83dc2f', - 'myuser@example.com', - (TIMESTAMPTZ '2013-03-26T17:50:06Z') - ); -``` - -Flyway also allows repeatable migrations that can be Java or Scala based: - -```scala -class R__AddCurrentUpdatedAtTimestamp extends JdbcMigration { - - override def migrate(c: Connection): Unit = { - val statement = c.prepareStatement("UPDATE users SET updated_at = ?") - try { - statement.setTimestamp(1, new Timestamp(System.currentTimeMillis())) - statement.execute() - } finally { - statement.close() - } - } -} -``` - -Start up `sbt` and go into the flyway module to run database migrations: +## Migration ``` +sbt +clean project flyway flywayMigrate ``` -See [the flyways documentation](http://flywaydb.org/getstarted/firststeps/sbt.html) for more details. - -## User DAO - -The module is defined with a public API in "modules/api" -- everything returns a Future, and there are custom [Joda Time](http://www.joda.org/joda-time/) `DateTime` objects which aren't mapped through Slick by default. - -```scala -trait UserDAO { - - def lookup(id: UUID)(implicit ec: UserDAOExecutionContext): Future[Option[User]] - - def all(implicit ec: UserDAOExecutionContext): Future[Seq[User]] - - def update(user: User)(implicit ec: UserDAOExecutionContext): Future[Int] - - def delete(id: UUID)(implicit ec: UserDAOExecutionContext): Future[Int] - - def create(user: User)(implicit ec: UserDAOExecutionContext): Future[Int] - - def close(): Future[Unit] -} - -case class User(id: UUID, email: String, createdAt: DateTime, updatedAt: Option[DateTime]) - -trait UserDAOExecutionContext extends ExecutionContext -``` - -## Slick - -The Postgres Driver for Slick is configured with [slick-pg](https://github.com/tminglei/slick-pg), which allows for custom mapping between PostgreSQL data types and Joda Time data types: - -```scala -trait MyPostgresDriver extends ExPostgresDriver -with PgArraySupport -with PgDateSupportJoda -with PgPlayJsonSupport { - - object MyAPI extends API with DateTimeImplicits with JsonImplicits { - implicit val strListTypeMapper = new SimpleArrayJdbcType[String]("text").to(_.toList) - implicit val playJsonArrayTypeMapper = - new AdvancedArrayJdbcType[JsValue](pgjson, - (s) => utils.SimpleArrayUtils.fromString[JsValue](Json.parse(_))(s).orNull, - (v) => utils.SimpleArrayUtils.mkString[JsValue](_.toString())(v) - ).to(_.toList) - } - - // jsonb support is in postgres 9.4.0 onward; for 9.3.x use "json" - def pgjson = "jsonb" - - override val api = MyAPI -} - -object MyPostgresDriver extends MyPostgresDriver -``` - -Slick configuration is simple, because the Slick [schema code generation](http://slick.typesafe.com/doc/3.1.0/code-generation.html) will look at the tables created from Flyway, and automatically generate a `Tables` trait. From there, `UsersRow` and `Users` are created automatically. Some conversion code is necessary to map between `UsersRow` and `User`. - -```scala -@Singleton -class SlickUserDAO @Inject()(db: Database) extends UserDAO with Tables { - - // Use the custom postgresql driver. - override val profile: JdbcProfile = MyPostgresDriver - - import profile.api._ - - private val queryById = Compiled( - (id: Rep[UUID]) => Users.filter(_.id === id)) - - def lookup(id: UUID)(implicit ec: UserDAOExecutionContext): Future[Option[User]] = { - val f: Future[Option[UsersRow]] = db.run(queryById(id).result.headOption) - f.map(maybeRow => maybeRow.map(usersRowToUser(_))) - } - - def all(implicit ec: UserDAOExecutionContext): Future[Seq[User]] = { - val f = db.run(Users.result) - f.map(seq => seq.map(usersRowToUser(_))) - } - - def update(user: User)(implicit ec: UserDAOExecutionContext): Future[Int] = { - db.run(queryById(user.id).update(userToUsersRow(user))) - } - - def delete(id: UUID)(implicit ec: UserDAOExecutionContext): Future[Int] = { - db.run(queryById(id).delete) - } - - def create(user: User)(implicit ec: UserDAOExecutionContext): Future[Int] = { - db.run( - Users += userToUsersRow(user.copy(createdAt = DateTime.now())) - ) - } - - def close(): Future[Unit] = { - Future.successful(db.close()) - } - - private def userToUsersRow(user:User): UsersRow = { - UsersRow(user.id, user.email, user.createdAt, user.updatedAt) - } - - private def usersRowToUser(usersRow:UsersRow): User = { - User(usersRow.id, usersRow.email, usersRow.createdAt, usersRow.updatedAt) - } -} -``` - -Once `SlickUserDAO` is compiled, everything is available to be bound and run in the Play application. - -To run table generation changes manually: - -```scala -genChanges -``` - -## Play - -The root `Module.scala` file contains all the classes need to bind Slick and expose it as a `UserDAO`: - -```scala -class Module(environment: Environment, - configuration: Configuration) extends AbstractModule { - override def configure(): Unit = { - - bind(classOf[Config]).toInstance(configuration.underlying) - bind(classOf[UserDAOExecutionContext]).toProvider(classOf[SlickUserDAOExecutionContextProvider]) - - bind(classOf[slick.jdbc.JdbcBackend.Database]).toProvider(classOf[DatabaseProvider]) - bind(classOf[UserDAO]).to(classOf[SlickUserDAO]) - - bind(classOf[UserDAOCloseHook]).asEagerSingleton() - } -} -``` - -There are a couple of providers to do a "lazy get" of the database and execution context from configuration: - -```scala -@Singleton -class DatabaseProvider @Inject() (config: Config) extends Provider[slick.jdbc.JdbcBackend.Database] { - - private val db = slick.jdbc.JdbcBackend.Database.forConfig("myapp.database", config) - - override def get(): slick.jdbc.JdbcBackend.Database = db -} - -@Singleton -class SlickUserDAOExecutionContextProvider @Inject() (actorSystem: akka.actor.ActorSystem) extends Provider[UserDAOExecutionContext] { - private val instance = { - val ec = actorSystem.dispatchers.lookup("myapp.database-dispatcher") - new SlickUserDAOExecutionContext(ec) - } - - override def get() = instance -} - -class SlickUserDAOExecutionContext(ec: ExecutionContext) extends UserDAOExecutionContext { - override def execute(runnable: Runnable): Unit = ec.execute(runnable) - - override def reportFailure(cause: Throwable): Unit = ec.reportFailure(cause) -} -``` - -The DAO must be closed to release JDBC connections, and this is handled through `UserDAOCloseHook`: - -```scala -class UserDAOCloseHook @Inject()(dao: UserDAO, lifecycle: ApplicationLifecycle) { - private val logger = org.slf4j.LoggerFactory.getLogger("application") - - lifecycle.addStopHook { () => - Future.successful { - logger.info("Now closing database connections!") - dao.close() - } - } -} -``` - -From there, the controller code is simple: - -```scala -@Singleton -class HomeController @Inject() (userDAO: UserDAO, userDAOExecutionContext: UserDAOExecutionContext) extends Controller { - - private val logger = org.slf4j.LoggerFactory.getLogger(this.getClass) - - implicit val ec = userDAOExecutionContext - - def index = Action.async { - logger.info("Calling index") - userDAO.all.map { users => - logger.info(s"Calling index: users = ${users}") - Ok(views.html.index(users)) - } - } -} -``` - ## Running To run the project, start up Play: diff --git a/app/Module.scala b/app/Module.scala index ee3ac696c..fcfcb7850 100644 --- a/app/Module.scala +++ b/app/Module.scala @@ -1,58 +1,46 @@ import javax.inject.{Inject, Provider, Singleton} +import akka.actor.ActorSystem import com.example.user.slick.SlickUserDAO import com.example.user.{UserDAO, UserDAOExecutionContext} import com.google.inject.AbstractModule import com.typesafe.config.Config import play.api.inject.ApplicationLifecycle +import play.api.libs.concurrent.CustomExecutionContext import play.api.{Configuration, Environment} +import slick.jdbc.JdbcBackend.Database -import scala.concurrent.{ExecutionContext, Future} +import scala.concurrent.Future +/** + * This module handles the bindings for the API to the Slick implementation. + * + * https://www.playframework.com/documentation/latest/ScalaDependencyInjection#Programmatic-bindings + */ class Module(environment: Environment, configuration: Configuration) extends AbstractModule { override def configure(): Unit = { - bind(classOf[UserDAOExecutionContext]).toProvider(classOf[SlickUserDAOExecutionContextProvider]) - - bind(classOf[slick.jdbc.JdbcBackend.Database]).toProvider(classOf[DatabaseProvider]) + bind(classOf[Database]).toProvider(classOf[DatabaseProvider]) bind(classOf[UserDAO]).to(classOf[SlickUserDAO]) - + bind(classOf[UserDAOExecutionContext]).to(classOf[SlickUserDAOExecutionContext]) bind(classOf[UserDAOCloseHook]).asEagerSingleton() } } @Singleton -class DatabaseProvider @Inject() (config: Config) extends Provider[slick.jdbc.JdbcBackend.Database] { - - private val db = slick.jdbc.JdbcBackend.Database.forConfig("myapp.database", config) - - override def get(): slick.jdbc.JdbcBackend.Database = db -} - -@Singleton -class SlickUserDAOExecutionContextProvider @Inject() (actorSystem: akka.actor.ActorSystem) extends Provider[UserDAOExecutionContext] { - private val instance = { - val ec = actorSystem.dispatchers.lookup("myapp.database-dispatcher") - new SlickUserDAOExecutionContext(ec) - } - - override def get() = instance +class DatabaseProvider @Inject() (config: Config) extends Provider[Database] { + lazy val get = Database.forConfig("myapp.database", config) } -class SlickUserDAOExecutionContext(ec: ExecutionContext) extends UserDAOExecutionContext { - override def execute(runnable: Runnable): Unit = ec.execute(runnable) - - override def reportFailure(cause: Throwable): Unit = ec.reportFailure(cause) -} +// Use a custom execution context +// https://www.playframework.com/documentation/latest/ScalaAsync#Creating-non-blocking-actions +class SlickUserDAOExecutionContext @Inject()(actorSystem: ActorSystem) + extends CustomExecutionContext(actorSystem, "myapp.database-dispatcher") + with UserDAOExecutionContext /** Closes database connections safely. Important on dev restart. */ class UserDAOCloseHook @Inject()(dao: UserDAO, lifecycle: ApplicationLifecycle) { - private val logger = org.slf4j.LoggerFactory.getLogger("application") - lifecycle.addStopHook { () => - Future.successful { - logger.info("Now closing database connections!") - dao.close() - } + Future.successful(dao.close()) } } diff --git a/app/controllers/HomeController.scala b/app/controllers/HomeController.scala index 6990c0565..9f82b47e3 100644 --- a/app/controllers/HomeController.scala +++ b/app/controllers/HomeController.scala @@ -1,25 +1,19 @@ package controllers -import java.util.UUID import javax.inject.{Inject, Singleton} -import akka.actor.ActorSystem -import com.example.user.{User, UserDAO, UserDAOExecutionContext} +import com.example.user.UserDAO import play.api.mvc._ -@Singleton -class HomeController @Inject() (userDAO: UserDAO, - userDAOExecutionContext: UserDAOExecutionContext, - cc: ControllerComponents) extends AbstractController(cc) { - - private val logger = org.slf4j.LoggerFactory.getLogger(this.getClass) +import scala.concurrent.ExecutionContext - implicit val ec = userDAOExecutionContext +@Singleton +class HomeController @Inject() (userDAO: UserDAO, cc: ControllerComponents) + (implicit ec: ExecutionContext) + extends AbstractController(cc) { def index = Action.async { implicit request => - logger.info("Calling index") userDAO.all.map { users => - logger.info(s"Calling index: users = ${users}") Ok(views.html.index(users)) } } diff --git a/build.sbt b/build.sbt index 022998c01..c371d8f96 100644 --- a/build.sbt +++ b/build.sbt @@ -1,16 +1,9 @@ - name := """play-isolated-slick""" version := "1.1-SNAPSHOT" scalaVersion in ThisBuild := "2.11.8" -initialize := { - val _ = initialize.value - if (sys.props("java.specification.version") != "1.8") - sys.error("Java 8 is required for this project.") -} - lazy val flyway = (project in file("modules/flyway")) .enablePlugins(FlywayPlugin) @@ -24,10 +17,14 @@ lazy val slick = (project in file("modules/slick")) lazy val root = (project in file(".")) .enablePlugins(PlayScala) - .settings( - libraryDependencies += guice, - // Adding this means no explicit import in *.scala.html files - TwirlKeys.templateImports += "com.example.user.User" - ).aggregate(api, slick) - .dependsOn(api, slick, flyway) + .aggregate(slick) + .dependsOn(slick, flyway) + +TwirlKeys.templateImports += "com.example.user.User" + +fork in Test := true + +libraryDependencies += guice +libraryDependencies += "com.h2database" % "h2" % "1.4.192" +libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "2.0.0-M2" % Test diff --git a/conf/application.conf b/conf/application.conf deleted file mode 100644 index c3ff64bae..000000000 --- a/conf/application.conf +++ /dev/null @@ -1,11 +0,0 @@ -myapp.database-dispatcher { - type = Dispatcher - executor = "thread-pool-executor" - thread-pool-executor { - // should be same size as connection pool - // see https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing - // http://letitcrash.com/post/40755146949/tuning-dispatchers-in-akka-applications - core-pool-size-min = 10 // minimumIdle - core-pool-size-max = 10 // maximumPoolSize - } -} diff --git a/conf/logback.xml b/conf/logback.xml index 3f18878ff..de7e962a3 100644 --- a/conf/logback.xml +++ b/conf/logback.xml @@ -1,45 +1,39 @@ - + - - ${application.home}/logs/application.log - - %date [%level] from %logger in %thread - %message%n%xException - - + + ${application.home:-.}/logs/application.log + + %date [%level] from %logger in %thread - %message%n%xException + + - - - %coloredLevel %logger{15} - %message%n%xException{10} - - + + + %coloredLevel %logger{15} - %message%n%xException{10} + + - - - + + + - - - + + + - - + + - - - - - - - - - - - - - - + + + + + + + + diff --git a/modules/api/src/main/scala/com/example/user/UserDAO.scala b/modules/api/src/main/scala/com/example/user/UserDAO.scala index a9f44b3f3..94cbe6a8b 100644 --- a/modules/api/src/main/scala/com/example/user/UserDAO.scala +++ b/modules/api/src/main/scala/com/example/user/UserDAO.scala @@ -1,8 +1,6 @@ package com.example.user -import java.util.UUID - -import org.joda.time.{DateTime, Instant} +import org.joda.time.DateTime import scala.concurrent.{ExecutionContext, Future} @@ -11,26 +9,23 @@ import scala.concurrent.{ExecutionContext, Future} */ trait UserDAO { - def lookup(id: UUID)(implicit ec: UserDAOExecutionContext): Future[Option[User]] + def lookup(id: String): Future[Option[User]] - def all(implicit ec: UserDAOExecutionContext): Future[Seq[User]] + def all: Future[Seq[User]] - def update(user: User)(implicit ec: UserDAOExecutionContext): Future[Int] + def update(user: User): Future[Int] - def delete(id: UUID)(implicit ec: UserDAOExecutionContext): Future[Int] + def delete(id: String): Future[Int] - def create(user: User)(implicit ec: UserDAOExecutionContext): Future[Int] + def create(user: User): Future[Int] def close(): Future[Unit] } /** * Implementation independent aggregate root. - * - * Note that this uses Joda Time classes and UUID, which are specifically mapped - * through the custom postgres driver. */ -case class User(id: UUID, email: String, createdAt: DateTime, updatedAt: Option[DateTime]) +case class User(id: String, email: String, createdAt: DateTime, updatedAt: Option[DateTime]) /** * Type safe execution context for operations on UserDAO. diff --git a/modules/flyway/build.sbt b/modules/flyway/build.sbt index a397aa44f..83a7fb062 100644 --- a/modules/flyway/build.sbt +++ b/modules/flyway/build.sbt @@ -10,9 +10,9 @@ libraryDependencies += "org.flywaydb" % "flyway-core" % "4.0" -lazy val databaseUrl = sys.env.getOrElse("DB_DEFAULT_URL", "jdbc:postgresql://localhost:5432/myapp") -lazy val databaseUser = sys.env.getOrElse("DB_DEFAULT_USER", "myuser") -lazy val databasePassword = sys.env.getOrElse("DB_DEFAULT_PASSWORD", "mypass") +lazy val databaseUrl = sys.env.getOrElse("DB_DEFAULT_URL", "jdbc:h2:./test") +lazy val databaseUser = sys.env.getOrElse("DB_DEFAULT_USER", "sa") +lazy val databasePassword = sys.env.getOrElse("DB_DEFAULT_PASSWORD", "") flywayLocations := Seq("classpath:db/migration") diff --git a/modules/flyway/src/main/resources/db/migration/V20150409112518__create_users_table.sql b/modules/flyway/src/main/resources/db/migration/V20150409112518__create_users_table.sql index fe522b159..8ac824164 100644 --- a/modules/flyway/src/main/resources/db/migration/V20150409112518__create_users_table.sql +++ b/modules/flyway/src/main/resources/db/migration/V20150409112518__create_users_table.sql @@ -1,6 +1,6 @@ create table "users" ( - "id" UUID PRIMARY KEY NOT NULL, + "id" VARCHAR(255) PRIMARY KEY NOT NULL, "email" VARCHAR(1024) NOT NULL, - created_at TIMESTAMPTZ NOT NULL, - updated_at TIMESTAMPTZ NULL + created_at TIMESTAMP NOT NULL, + updated_at TIMESTAMP NULL ); diff --git a/modules/flyway/src/main/resources/db/migration/V20150409131208__add_user.sql b/modules/flyway/src/main/resources/db/migration/V20150409131208__add_user.sql index 17ef1fe36..c9c74a481 100644 --- a/modules/flyway/src/main/resources/db/migration/V20150409131208__add_user.sql +++ b/modules/flyway/src/main/resources/db/migration/V20150409131208__add_user.sql @@ -1,5 +1,5 @@ INSERT INTO "users" VALUES ( 'd074bce8-a8ca-49ec-9225-a50ffe83dc2f', 'myuser@example.com', - (TIMESTAMPTZ '2013-03-26T17:50:06Z') + (TIMESTAMP '2013-03-26T17:50:06Z'), (TIMESTAMP '2013-03-26T17:50:06Z') ); diff --git a/modules/flyway/src/main/scala/db/migration/R__AddCurrentUpdatedAtTimestamp.scala b/modules/flyway/src/main/scala/db/migration/R__AddCurrentUpdatedAtTimestamp.scala deleted file mode 100644 index ec46e0736..000000000 --- a/modules/flyway/src/main/scala/db/migration/R__AddCurrentUpdatedAtTimestamp.scala +++ /dev/null @@ -1,24 +0,0 @@ -package db.migration - -import java.sql.{Connection, Timestamp} - -import org.flywaydb.core.api.migration.jdbc.JdbcMigration - -/** - * Adds a repeatable Flyway migration in Scala. - * - * See Java migrations - * for more details. - */ -class R__AddCurrentUpdatedAtTimestamp extends JdbcMigration { - - override def migrate(c: Connection): Unit = { - val statement = c.prepareStatement("UPDATE users SET updated_at = ?") - try { - statement.setTimestamp(1, new Timestamp(System.currentTimeMillis())) - statement.execute() - } finally { - statement.close() - } - } -} diff --git a/modules/slick/build.sbt b/modules/slick/build.sbt index 19a38eb08..f34c658ee 100644 --- a/modules/slick/build.sbt +++ b/modules/slick/build.sbt @@ -5,23 +5,19 @@ libraryDependencies ++= Seq( "com.zaxxer" % "HikariCP" % "2.4.1", "com.typesafe.slick" %% "slick" % "3.1.1", "com.typesafe.slick" %% "slick-hikaricp" % "3.1.1", - "org.postgresql" % "postgresql" % "9.4-1201-jdbc41", - "com.github.tminglei" %% "slick-pg" % "0.12.0", - "com.github.tminglei" %% "slick-pg_play-json" % "0.12.0", - "com.github.tminglei" %% "slick-pg_joda-time" % "0.12.0", - "com.github.tototoshi" %% "slick-joda-mapper" % "2.1.0" + "com.github.tototoshi" %% "slick-joda-mapper" % "2.2.0" ) -lazy val databaseUrl = sys.env.getOrElse("DB_DEFAULT_URL", "jdbc:postgresql:myapp") -lazy val databaseUser = sys.env.getOrElse("DB_DEFAULT_USER", "myuser") -lazy val databasePassword = sys.env.getOrElse("DB_DEFAULT_PASSWORD", "mypass") +lazy val databaseUrl = sys.env.getOrElse("DB_DEFAULT_URL", "jdbc:h2:./test") +lazy val databaseUser = sys.env.getOrElse("DB_DEFAULT_USER", "sa") +lazy val databasePassword = sys.env.getOrElse("DB_DEFAULT_PASSWORD", "") slickCodegenSettings slickCodegenDatabaseUrl := databaseUrl slickCodegenDatabaseUser := databaseUser slickCodegenDatabasePassword := databasePassword -slickCodegenDriver := slick.driver.PostgresDriver -slickCodegenJdbcDriver := "org.postgresql.Driver" +slickCodegenDriver := slick.driver.H2Driver +slickCodegenJdbcDriver := "org.h2.Driver" slickCodegenOutputPackage := "com.example.user.slick" slickCodegenExcludedTables := Seq("schema_version") diff --git a/modules/slick/src/main/resources/application.conf b/modules/slick/src/main/resources/application.conf index 1d86b65fe..897532e3b 100644 --- a/modules/slick/src/main/resources/application.conf +++ b/modules/slick/src/main/resources/application.conf @@ -1,20 +1,39 @@ myapp = { database = { - driver = org.postgresql.Driver - url = "jdbc:postgresql://localhost:5432/myapp" - user = "myuser" - password = "mypass" - numThreads = 10 + driver = org.h2.Driver + url = "jdbc:h2:./test" + user = "sa" + password = "" + + // The number of threads determines how many things you can *run* in parallel + // the number of connections determines you many things you can *keep in memory* at the same time + // on the database server. + // numThreads = (core_count (hyperthreading included)) + numThreads = 4 + + // queueSize = ((core_count * 2) + effective_spindle_count) + // on a MBP 13, this is 2 cores * 2 (hyperthreading not included) + 1 hard disk + queueSize = 5 + + // https://blog.knoldus.com/2016/01/01/best-practices-for-using-slick-on-production/ + // make larger than numThreads + queueSize + maxConnections = 10 + connectionTimeout = 5000 validationTimeout = 5000 } +} - dispatcher { - fork-join-executor { - parallelism-factor = 2 - parallelism-max = 20 - } +// The custom database dispatcher should match the number of threads allocated to Slick +// https://www.playframework.com/documentation/latest/ThreadPools +myapp.database-dispatcher { + type = Dispatcher + executor = "thread-pool-executor" + thread-pool-executor { + core-pool-size-min = 4 + core-pool-size-max = 4 } } + diff --git a/modules/slick/src/main/scala/com/example/user/slick/MyPostgresDriver.scala b/modules/slick/src/main/scala/com/example/user/slick/MyPostgresDriver.scala deleted file mode 100644 index b22be7d10..000000000 --- a/modules/slick/src/main/scala/com/example/user/slick/MyPostgresDriver.scala +++ /dev/null @@ -1,30 +0,0 @@ -package com.example.user.slick - -import com.github.tminglei.slickpg._ -import play.api.libs.json.{JsValue, Json} - -/** - * A postgresql driver with extended Joda and JSON support. - */ -trait MyPostgresDriver extends ExPostgresDriver -with PgArraySupport -with PgDateSupportJoda -with PgPlayJsonSupport { - - object MyAPI extends API with DateTimeImplicits with JsonImplicits { - implicit val strListTypeMapper = new SimpleArrayJdbcType[String]("text").to(_.toList) - implicit val playJsonArrayTypeMapper = - new AdvancedArrayJdbcType[JsValue](pgjson, - (s) => utils.SimpleArrayUtils.fromString[JsValue](Json.parse(_))(s).orNull, - (v) => utils.SimpleArrayUtils.mkString[JsValue](_.toString())(v) - ).to(_.toList) - } - - // jsonb support is in postgres 9.4.0 onward; for 9.3.x use "json" - def pgjson = "jsonb" - - override val api = MyAPI -} - -object MyPostgresDriver extends MyPostgresDriver - diff --git a/modules/slick/src/main/scala/com/example/user/slick/SlickUserDAO.scala b/modules/slick/src/main/scala/com/example/user/slick/SlickUserDAO.scala index dbb92f766..ba7f73f57 100644 --- a/modules/slick/src/main/scala/com/example/user/slick/SlickUserDAO.scala +++ b/modules/slick/src/main/scala/com/example/user/slick/SlickUserDAO.scala @@ -1,6 +1,5 @@ package com.example.user.slick -import java.util.UUID import javax.inject.{Inject, Singleton} import org.joda.time.DateTime @@ -17,35 +16,34 @@ import scala.language.implicitConversions * Note that you must run "flyway/flywayMigrate" before "compile" here. */ @Singleton -class SlickUserDAO @Inject()(db: Database) extends UserDAO with Tables { +class SlickUserDAO @Inject()(db: Database)(implicit ec: UserDAOExecutionContext) extends UserDAO with Tables { - // Use the custom postgresql driver. - override val profile: JdbcProfile = MyPostgresDriver + override val profile: JdbcProfile = _root_.slick.driver.H2Driver import profile.api._ private val queryById = Compiled( - (id: Rep[UUID]) => Users.filter(_.id === id)) + (id: Rep[String]) => Users.filter(_.id === id)) - def lookup(id: UUID)(implicit ec: UserDAOExecutionContext): Future[Option[User]] = { + def lookup(id: String): Future[Option[User]] = { val f: Future[Option[UsersRow]] = db.run(queryById(id).result.headOption) - f.map(maybeRow => maybeRow.map(usersRowToUser(_))) + f.map(maybeRow => maybeRow.map(usersRowToUser)) } - def all(implicit ec: UserDAOExecutionContext): Future[Seq[User]] = { + def all: Future[Seq[User]] = { val f = db.run(Users.result) - f.map(seq => seq.map(usersRowToUser(_))) + f.map(seq => seq.map(usersRowToUser)) } - def update(user: User)(implicit ec: UserDAOExecutionContext): Future[Int] = { + def update(user: User): Future[Int] = { db.run(queryById(user.id).update(userToUsersRow(user))) } - def delete(id: UUID)(implicit ec: UserDAOExecutionContext): Future[Int] = { + def delete(id: String): Future[Int] = { db.run(queryById(id).delete) } - def create(user: User)(implicit ec: UserDAOExecutionContext): Future[Int] = { + def create(user: User): Future[Int] = { db.run( Users += userToUsersRow(user.copy(createdAt = DateTime.now())) ) diff --git a/project/Build.scala b/project/Build.scala index 67e94e8a9..da9c6f29d 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -23,7 +23,7 @@ object Common { libraryDependencies ++= Seq( "javax.inject" % "javax.inject" % "1", "joda-time" % "joda-time" % "2.9.2", - "org.joda" % "joda-convert" % "1.2", + "org.joda" % "joda-convert" % "1.6", "com.google.inject" % "guice" % "4.0" ), scalacOptions in Test ++= Seq("-Yrangepos") diff --git a/project/plugins.sbt b/project/plugins.sbt index a45e1b594..b9e727ee9 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -11,7 +11,7 @@ addSbtPlugin("org.flywaydb" % "flyway-sbt" % "4.0") // https://github.com/tototoshi/sbt-slick-codegen addSbtPlugin("com.github.tototoshi" % "sbt-slick-codegen" % "1.2.0") -libraryDependencies += "org.postgresql" % "postgresql" % "9.4-1201-jdbc41" +libraryDependencies += "com.h2database" % "h2" % "1.4.192" // The Play plugin addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-M1") diff --git a/test/controller/FunctionalSpec.scala b/test/controller/FunctionalSpec.scala new file mode 100644 index 000000000..9e1b89f49 --- /dev/null +++ b/test/controller/FunctionalSpec.scala @@ -0,0 +1,21 @@ +package controller + +import org.scalatestplus.play.{BaseOneAppPerSuite, PlaySpec} +import play.api.test.FakeRequest +import play.api.test.Helpers._ + +/** + * Runs a functional test with the application, using an in memory + * database. Migrations are handled automatically by play-flyway + */ +class FunctionalSpec extends PlaySpec with BaseOneAppPerSuite with MyApplicationFactory { + + "HomeController" should { + + "work with in memory h2 database" in { + val future = route(app, FakeRequest(GET, "/")).get + contentAsString(future) must include("myuser@example.com") + } + } + +} diff --git a/test/controller/MyApplicationFactory.scala b/test/controller/MyApplicationFactory.scala new file mode 100644 index 000000000..489577e78 --- /dev/null +++ b/test/controller/MyApplicationFactory.scala @@ -0,0 +1,42 @@ +package controller + +import com.google.inject.Inject +import org.flywaydb.core.Flyway +import org.flywaydb.core.internal.util.jdbc.DriverDataSource +import org.scalatestplus.play.FakeApplicationFactory +import play.api.inject.guice.GuiceApplicationBuilder +import play.api.inject.{Binding, Module} +import play.api.{Application, Configuration, Environment} + +/** + * Set up an application factory that runs flyways migrations on in memory database. + */ +trait MyApplicationFactory extends FakeApplicationFactory { + def fakeApplication(): Application = { + new GuiceApplicationBuilder() + .configure(Map("myapp.database.url" -> "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1")) + .bindings(new FlywayModule) + .build() + } +} + +class FlywayModule extends Module { + override def bindings(environment: Environment, configuration: Configuration): Seq[Binding[_]] = { + Seq(bind[FlywayMigrator].toSelf.eagerly() ) + } +} + +class FlywayMigrator @Inject()(env: Environment, configuration: Configuration) { + def onStart(): Unit = { + val driver = configuration.get[String]("myapp.database.driver") + val url = configuration.get[String]("myapp.database.url") + val user = configuration.get[String]("myapp.database.user") + val password = configuration.get[String]("myapp.database.password") + val flyway = new Flyway + flyway.setDataSource(new DriverDataSource(env.classLoader, driver, url, user, password)) + flyway.setLocations("classpath:db/migration") + flyway.migrate() + } + + onStart() +} From ae605250369e4be992612d56253570ac981886e3 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Sun, 26 Feb 2017 16:45:50 -0800 Subject: [PATCH 14/53] Fix travis ci --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 5eb091632..24ccc770e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,8 @@ scala: - 2.11.8 jdk: - oraclejdk8 +script: + sbt ++$TRAVIS_SCALA_VERSION flyway:flywayMigrate compile test cache: directories: - "$HOME/.ivy2/cache" From 9bc1f2510b6d243272d505fd505a63bd6ab66945 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Sun, 26 Feb 2017 17:32:59 -0800 Subject: [PATCH 15/53] More updates --- README.md | 11 +++++------ build.sbt | 8 +++++--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 09c8473ad..8fcfbd444 100644 --- a/README.md +++ b/README.md @@ -5,20 +5,19 @@ This project shows Play working with Slick. This project is configured to keep all the modules self-contained. * Slick is isolated from Play, not using play-slick. -* Database migration is done using [Flyway](), not Play Evolutions. +* Database migration is done using [Flyway](https://flywaydb.org/), not Play Evolutions. * Slick's classes are auto-generated following database migration. -## Migration +## Database Migration ``` -sbt -clean -project flyway -flywayMigrate +sbt flyway/flywayMigrate ``` ## Running +You will need to run the flywayMigrate task first, and then you will be able to generate tables using sbt-codegen. + To run the project, start up Play: ``` diff --git a/build.sbt b/build.sbt index c371d8f96..25c24042b 100644 --- a/build.sbt +++ b/build.sbt @@ -18,13 +18,15 @@ lazy val slick = (project in file("modules/slick")) lazy val root = (project in file(".")) .enablePlugins(PlayScala) .aggregate(slick) - .dependsOn(slick, flyway) + .dependsOn(slick) TwirlKeys.templateImports += "com.example.user.User" -fork in Test := true - libraryDependencies += guice libraryDependencies += "com.h2database" % "h2" % "1.4.192" + +// Automatic database migration available in testing +fork in Test := true +libraryDependencies += "org.flywaydb" % "flyway-core" % "4.0" % Test libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "2.0.0-M2" % Test From e198021f124c7af2aa53c5fd19f593c6a654f813 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Sun, 26 Feb 2017 18:00:12 -0800 Subject: [PATCH 16/53] More tweaks --- .travis.yml | 2 +- README.md | 17 ++++++++++++++++- .../db/migration/V20150409131208__add_user.sql | 7 ++++--- test/controller/MyApplicationFactory.scala | 2 +- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 24ccc770e..04488aaea 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ scala: jdk: - oraclejdk8 script: - sbt ++$TRAVIS_SCALA_VERSION flyway:flywayMigrate compile test + sbt ++$TRAVIS_SCALA_VERSION clean flyway/flywayMigrate slickCodegen compile test cache: directories: - "$HOME/.ivy2/cache" diff --git a/README.md b/README.md index 8fcfbd444..7955d4a13 100644 --- a/README.md +++ b/README.md @@ -14,16 +14,31 @@ This project is configured to keep all the modules self-contained. sbt flyway/flywayMigrate ``` -## Running +## Slick Code Generation You will need to run the flywayMigrate task first, and then you will be able to generate tables using sbt-codegen. +``` +sbt slickCodegen +``` + +## Testing + +You can run functional tests against an in memory database and Slick easily with Play from a clean slate: + +``` +sbt clean flyway/flywayMigrate slickCodegen compile test +``` + +## Running + To run the project, start up Play: ``` sbt run ``` + And that's it! Now go to [http://localhost:9000](http://localhost:9000), and you will see the list of users in the database. diff --git a/modules/flyway/src/main/resources/db/migration/V20150409131208__add_user.sql b/modules/flyway/src/main/resources/db/migration/V20150409131208__add_user.sql index c9c74a481..7113f28ab 100644 --- a/modules/flyway/src/main/resources/db/migration/V20150409131208__add_user.sql +++ b/modules/flyway/src/main/resources/db/migration/V20150409131208__add_user.sql @@ -1,5 +1,6 @@ INSERT INTO "users" VALUES ( 'd074bce8-a8ca-49ec-9225-a50ffe83dc2f', - 'myuser@example.com', - (TIMESTAMP '2013-03-26T17:50:06Z'), (TIMESTAMP '2013-03-26T17:50:06Z') - ); + 'myuser@example.com', + (TIMESTAMP '2013-03-26T17:50:06Z'), + (TIMESTAMP '2013-03-26T17:50:06Z') +); diff --git a/test/controller/MyApplicationFactory.scala b/test/controller/MyApplicationFactory.scala index 489577e78..62606313e 100644 --- a/test/controller/MyApplicationFactory.scala +++ b/test/controller/MyApplicationFactory.scala @@ -34,7 +34,7 @@ class FlywayMigrator @Inject()(env: Environment, configuration: Configuration) { val password = configuration.get[String]("myapp.database.password") val flyway = new Flyway flyway.setDataSource(new DriverDataSource(env.classLoader, driver, url, user, password)) - flyway.setLocations("classpath:db/migration") + flyway.setLocations("filesystem:modules/flyway/src/main/resources/db/migration") flyway.migrate() } From 7eb605defe82fb6f790d3f02d2646163675084d8 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Mon, 27 Feb 2017 10:01:57 -0800 Subject: [PATCH 17/53] Use default execution context --- app/Module.scala | 11 +---------- .../api/src/main/scala/com/example/user/UserDAO.scala | 5 ----- .../scala/com/example/user/slick/SlickUserDAO.scala | 8 ++++++-- 3 files changed, 7 insertions(+), 17 deletions(-) diff --git a/app/Module.scala b/app/Module.scala index fcfcb7850..b4d328b02 100644 --- a/app/Module.scala +++ b/app/Module.scala @@ -1,12 +1,10 @@ import javax.inject.{Inject, Provider, Singleton} -import akka.actor.ActorSystem +import com.example.user.UserDAO import com.example.user.slick.SlickUserDAO -import com.example.user.{UserDAO, UserDAOExecutionContext} import com.google.inject.AbstractModule import com.typesafe.config.Config import play.api.inject.ApplicationLifecycle -import play.api.libs.concurrent.CustomExecutionContext import play.api.{Configuration, Environment} import slick.jdbc.JdbcBackend.Database @@ -22,7 +20,6 @@ class Module(environment: Environment, override def configure(): Unit = { bind(classOf[Database]).toProvider(classOf[DatabaseProvider]) bind(classOf[UserDAO]).to(classOf[SlickUserDAO]) - bind(classOf[UserDAOExecutionContext]).to(classOf[SlickUserDAOExecutionContext]) bind(classOf[UserDAOCloseHook]).asEagerSingleton() } } @@ -32,12 +29,6 @@ class DatabaseProvider @Inject() (config: Config) extends Provider[Database] { lazy val get = Database.forConfig("myapp.database", config) } -// Use a custom execution context -// https://www.playframework.com/documentation/latest/ScalaAsync#Creating-non-blocking-actions -class SlickUserDAOExecutionContext @Inject()(actorSystem: ActorSystem) - extends CustomExecutionContext(actorSystem, "myapp.database-dispatcher") - with UserDAOExecutionContext - /** Closes database connections safely. Important on dev restart. */ class UserDAOCloseHook @Inject()(dao: UserDAO, lifecycle: ApplicationLifecycle) { lifecycle.addStopHook { () => diff --git a/modules/api/src/main/scala/com/example/user/UserDAO.scala b/modules/api/src/main/scala/com/example/user/UserDAO.scala index 94cbe6a8b..85309bca8 100644 --- a/modules/api/src/main/scala/com/example/user/UserDAO.scala +++ b/modules/api/src/main/scala/com/example/user/UserDAO.scala @@ -26,8 +26,3 @@ trait UserDAO { * Implementation independent aggregate root. */ case class User(id: String, email: String, createdAt: DateTime, updatedAt: Option[DateTime]) - -/** - * Type safe execution context for operations on UserDAO. - */ -trait UserDAOExecutionContext extends ExecutionContext diff --git a/modules/slick/src/main/scala/com/example/user/slick/SlickUserDAO.scala b/modules/slick/src/main/scala/com/example/user/slick/SlickUserDAO.scala index ba7f73f57..9ffdfd3c4 100644 --- a/modules/slick/src/main/scala/com/example/user/slick/SlickUserDAO.scala +++ b/modules/slick/src/main/scala/com/example/user/slick/SlickUserDAO.scala @@ -7,16 +7,20 @@ import slick.driver.JdbcProfile import slick.jdbc.JdbcBackend.Database import com.example.user._ -import scala.concurrent.Future +import scala.concurrent.{ExecutionContext, Future} import scala.language.implicitConversions /** * A User DAO implemented with Slick, leveraging Slick code gen. * * Note that you must run "flyway/flywayMigrate" before "compile" here. + * + * @param db the slick database that this user DAO is using internally, bound through Module. + * @param ec a CPU bound execution context. Slick manages blocking JDBC calls with its + * own internal thread pool, so Play's default execution context is fine here. */ @Singleton -class SlickUserDAO @Inject()(db: Database)(implicit ec: UserDAOExecutionContext) extends UserDAO with Tables { +class SlickUserDAO @Inject()(db: Database)(implicit ec: ExecutionContext) extends UserDAO with Tables { override val profile: JdbcProfile = _root_.slick.driver.H2Driver From d7c86be45a4866d243fd589d5b239fb7adc34702 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Tue, 7 Mar 2017 21:19:21 -0800 Subject: [PATCH 18/53] Updated with template-control on 2017-03-08T05:19:21Z /LICENSE: wrote /LICENSE **/build.sbt: scalaVersion := "2.12.1" --- LICENSE | 11 +++++------ build.sbt | 2 +- project/build.properties | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/LICENSE b/LICENSE index 4baedcb95..b018ae2bc 100644 --- a/LICENSE +++ b/LICENSE @@ -1,8 +1,7 @@ -This software is licensed under the Apache 2 license, quoted below. +License +------- +Written in 2016 by Lightbend -Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with -the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. +To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty. -Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an -"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific -language governing permissions and limitations under the License. \ No newline at end of file +You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see . diff --git a/build.sbt b/build.sbt index 25c24042b..2ef7d46d3 100644 --- a/build.sbt +++ b/build.sbt @@ -2,7 +2,7 @@ name := """play-isolated-slick""" version := "1.1-SNAPSHOT" -scalaVersion in ThisBuild := "2.11.8" +scalaVersion := "2.12.1" lazy val flyway = (project in file("modules/flyway")) .enablePlugins(FlywayPlugin) diff --git a/project/build.properties b/project/build.properties index 5f32afe7d..27e88aa11 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=0.13.13 \ No newline at end of file +sbt.version=0.13.13 From 720f69e940eae581c112d127d498a100af818c5c Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Tue, 28 Mar 2017 13:57:32 -0700 Subject: [PATCH 19/53] Updated with template-control on 2017-03-28T20:57:32.278Z /LICENSE: wrote /LICENSE **/build.sbt: libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "3.0.0-M2" % Test **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-M3") --- build.sbt | 2 +- project/plugins.sbt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index 2ef7d46d3..dc7f1fd6f 100644 --- a/build.sbt +++ b/build.sbt @@ -28,5 +28,5 @@ libraryDependencies += "com.h2database" % "h2" % "1.4.192" // Automatic database migration available in testing fork in Test := true libraryDependencies += "org.flywaydb" % "flyway-core" % "4.0" % Test -libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "2.0.0-M2" % Test +libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "3.0.0-M2" % Test diff --git a/project/plugins.sbt b/project/plugins.sbt index b9e727ee9..21871e6a2 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -14,4 +14,4 @@ addSbtPlugin("com.github.tototoshi" % "sbt-slick-codegen" % "1.2.0") libraryDependencies += "com.h2database" % "h2" % "1.4.192" // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-M1") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-M3") From beabb7e18cb7a8ca420ca961723c37789952e7fe Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Sat, 8 Apr 2017 21:09:11 -0700 Subject: [PATCH 20/53] Updated with template-control on 2017-04-09T04:09:11.172Z /LICENSE: wrote /LICENSE **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-M4") --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 21871e6a2..82bb44090 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -14,4 +14,4 @@ addSbtPlugin("com.github.tototoshi" % "sbt-slick-codegen" % "1.2.0") libraryDependencies += "com.h2database" % "h2" % "1.4.192" // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-M3") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-M4") From 3777df32e03565f33c95f6a89c83fa06c303b0a5 Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Mon, 10 Apr 2017 21:48:48 -0300 Subject: [PATCH 21/53] Update to play 2.6.0-M4 and also other dependencies --- build.sbt | 5 +++-- modules/slick/build.sbt | 2 +- project/Build.scala | 6 +++--- project/plugins.sbt | 2 +- test/controller/MyApplicationFactory.scala | 4 +++- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/build.sbt b/build.sbt index dc7f1fd6f..d8913980d 100644 --- a/build.sbt +++ b/build.sbt @@ -23,10 +23,11 @@ lazy val root = (project in file(".")) TwirlKeys.templateImports += "com.example.user.User" libraryDependencies += guice -libraryDependencies += "com.h2database" % "h2" % "1.4.192" +libraryDependencies += "com.h2database" % "h2" % "1.4.194" // Automatic database migration available in testing fork in Test := true -libraryDependencies += "org.flywaydb" % "flyway-core" % "4.0" % Test +libraryDependencies += "org.flywaydb" % "flyway-core" % "4.1.2" % Test +libraryDependencies += "com.typesafe.play" %% "play-ahc-ws" % "2.6.0-M4" % Test libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "3.0.0-M2" % Test diff --git a/modules/slick/build.sbt b/modules/slick/build.sbt index f34c658ee..42deeec3e 100644 --- a/modules/slick/build.sbt +++ b/modules/slick/build.sbt @@ -37,4 +37,4 @@ slickCodegenCodeGenerator := { (model: m.Model) => } } -sourceGenerators in Compile <+= slickCodegen +sourceGenerators in Compile += slickCodegen.taskValue diff --git a/project/Build.scala b/project/Build.scala index da9c6f29d..4e1685d09 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -22,9 +22,9 @@ object Common { Resolver.sonatypeRepo("snapshots")), libraryDependencies ++= Seq( "javax.inject" % "javax.inject" % "1", - "joda-time" % "joda-time" % "2.9.2", - "org.joda" % "joda-convert" % "1.6", - "com.google.inject" % "guice" % "4.0" + "joda-time" % "joda-time" % "2.9.9", + "org.joda" % "joda-convert" % "1.8.1", + "com.google.inject" % "guice" % "4.1.0" ), scalacOptions in Test ++= Seq("-Yrangepos") ) diff --git a/project/plugins.sbt b/project/plugins.sbt index 21871e6a2..82bb44090 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -14,4 +14,4 @@ addSbtPlugin("com.github.tototoshi" % "sbt-slick-codegen" % "1.2.0") libraryDependencies += "com.h2database" % "h2" % "1.4.192" // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-M3") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-M4") diff --git a/test/controller/MyApplicationFactory.scala b/test/controller/MyApplicationFactory.scala index 62606313e..d0e48263b 100644 --- a/test/controller/MyApplicationFactory.scala +++ b/test/controller/MyApplicationFactory.scala @@ -1,5 +1,7 @@ package controller +import java.util.Properties + import com.google.inject.Inject import org.flywaydb.core.Flyway import org.flywaydb.core.internal.util.jdbc.DriverDataSource @@ -33,7 +35,7 @@ class FlywayMigrator @Inject()(env: Environment, configuration: Configuration) { val user = configuration.get[String]("myapp.database.user") val password = configuration.get[String]("myapp.database.password") val flyway = new Flyway - flyway.setDataSource(new DriverDataSource(env.classLoader, driver, url, user, password)) + flyway.setDataSource(new DriverDataSource(env.classLoader, driver, url, user, password, new Properties())) flyway.setLocations("filesystem:modules/flyway/src/main/resources/db/migration") flyway.migrate() } From 34c311f3b49cd54a37240c4076e2c3bb4e57e20f Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Mon, 10 Apr 2017 21:48:48 -0300 Subject: [PATCH 22/53] Update to play 2.6.0-M4 and also other dependencies --- build.sbt | 5 +++-- modules/slick/build.sbt | 2 +- project/Build.scala | 6 +++--- test/controller/MyApplicationFactory.scala | 4 +++- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/build.sbt b/build.sbt index dc7f1fd6f..d8913980d 100644 --- a/build.sbt +++ b/build.sbt @@ -23,10 +23,11 @@ lazy val root = (project in file(".")) TwirlKeys.templateImports += "com.example.user.User" libraryDependencies += guice -libraryDependencies += "com.h2database" % "h2" % "1.4.192" +libraryDependencies += "com.h2database" % "h2" % "1.4.194" // Automatic database migration available in testing fork in Test := true -libraryDependencies += "org.flywaydb" % "flyway-core" % "4.0" % Test +libraryDependencies += "org.flywaydb" % "flyway-core" % "4.1.2" % Test +libraryDependencies += "com.typesafe.play" %% "play-ahc-ws" % "2.6.0-M4" % Test libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "3.0.0-M2" % Test diff --git a/modules/slick/build.sbt b/modules/slick/build.sbt index f34c658ee..42deeec3e 100644 --- a/modules/slick/build.sbt +++ b/modules/slick/build.sbt @@ -37,4 +37,4 @@ slickCodegenCodeGenerator := { (model: m.Model) => } } -sourceGenerators in Compile <+= slickCodegen +sourceGenerators in Compile += slickCodegen.taskValue diff --git a/project/Build.scala b/project/Build.scala index da9c6f29d..4e1685d09 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -22,9 +22,9 @@ object Common { Resolver.sonatypeRepo("snapshots")), libraryDependencies ++= Seq( "javax.inject" % "javax.inject" % "1", - "joda-time" % "joda-time" % "2.9.2", - "org.joda" % "joda-convert" % "1.6", - "com.google.inject" % "guice" % "4.0" + "joda-time" % "joda-time" % "2.9.9", + "org.joda" % "joda-convert" % "1.8.1", + "com.google.inject" % "guice" % "4.1.0" ), scalacOptions in Test ++= Seq("-Yrangepos") ) diff --git a/test/controller/MyApplicationFactory.scala b/test/controller/MyApplicationFactory.scala index 62606313e..d0e48263b 100644 --- a/test/controller/MyApplicationFactory.scala +++ b/test/controller/MyApplicationFactory.scala @@ -1,5 +1,7 @@ package controller +import java.util.Properties + import com.google.inject.Inject import org.flywaydb.core.Flyway import org.flywaydb.core.internal.util.jdbc.DriverDataSource @@ -33,7 +35,7 @@ class FlywayMigrator @Inject()(env: Environment, configuration: Configuration) { val user = configuration.get[String]("myapp.database.user") val password = configuration.get[String]("myapp.database.password") val flyway = new Flyway - flyway.setDataSource(new DriverDataSource(env.classLoader, driver, url, user, password)) + flyway.setDataSource(new DriverDataSource(env.classLoader, driver, url, user, password, new Properties())) flyway.setLocations("filesystem:modules/flyway/src/main/resources/db/migration") flyway.migrate() } From 56be6ea38e34f726b1fe0078123c892d69ee2668 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Thu, 13 Apr 2017 15:00:45 -0700 Subject: [PATCH 23/53] Update dependencies to 2.12 versions --- .travis.yml | 2 +- build.sbt | 2 +- modules/slick/build.sbt | 8 ++++---- .../main/scala/com/example/user/slick/SlickUserDAO.scala | 4 ++-- project/Build.scala | 5 ++--- project/build.properties | 2 +- 6 files changed, 11 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 04488aaea..fa1d24130 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: scala scala: -- 2.11.8 +- 2.12.1 jdk: - oraclejdk8 script: diff --git a/build.sbt b/build.sbt index d8913980d..8268d9188 100644 --- a/build.sbt +++ b/build.sbt @@ -29,5 +29,5 @@ libraryDependencies += "com.h2database" % "h2" % "1.4.194" fork in Test := true libraryDependencies += "org.flywaydb" % "flyway-core" % "4.1.2" % Test libraryDependencies += "com.typesafe.play" %% "play-ahc-ws" % "2.6.0-M4" % Test -libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "3.0.0-M2" % Test +libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "3.0.0-M3" % Test diff --git a/modules/slick/build.sbt b/modules/slick/build.sbt index 42deeec3e..2cf9168f8 100644 --- a/modules/slick/build.sbt +++ b/modules/slick/build.sbt @@ -2,10 +2,10 @@ import slick.codegen.SourceCodeGenerator import slick.{ model => m } libraryDependencies ++= Seq( - "com.zaxxer" % "HikariCP" % "2.4.1", - "com.typesafe.slick" %% "slick" % "3.1.1", - "com.typesafe.slick" %% "slick-hikaricp" % "3.1.1", - "com.github.tototoshi" %% "slick-joda-mapper" % "2.2.0" + "com.zaxxer" % "HikariCP" % "2.6.1", + "com.typesafe.slick" %% "slick" % "3.2.0", + "com.typesafe.slick" %% "slick-hikaricp" % "3.2.0", + "com.github.tototoshi" %% "slick-joda-mapper" % "2.3.0" ) lazy val databaseUrl = sys.env.getOrElse("DB_DEFAULT_URL", "jdbc:h2:./test") diff --git a/modules/slick/src/main/scala/com/example/user/slick/SlickUserDAO.scala b/modules/slick/src/main/scala/com/example/user/slick/SlickUserDAO.scala index 9ffdfd3c4..1060efaa3 100644 --- a/modules/slick/src/main/scala/com/example/user/slick/SlickUserDAO.scala +++ b/modules/slick/src/main/scala/com/example/user/slick/SlickUserDAO.scala @@ -3,7 +3,7 @@ package com.example.user.slick import javax.inject.{Inject, Singleton} import org.joda.time.DateTime -import slick.driver.JdbcProfile +import slick.jdbc.JdbcProfile import slick.jdbc.JdbcBackend.Database import com.example.user._ @@ -22,7 +22,7 @@ import scala.language.implicitConversions @Singleton class SlickUserDAO @Inject()(db: Database)(implicit ec: ExecutionContext) extends UserDAO with Tables { - override val profile: JdbcProfile = _root_.slick.driver.H2Driver + override val profile: JdbcProfile = _root_.slick.jdbc.H2Profile import profile.api._ diff --git a/project/Build.scala b/project/Build.scala index 4e1685d09..8cd71b9ba 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -4,7 +4,7 @@ import sbt.{Resolver, _} object Common { def projectSettings = Seq( - scalaVersion := "2.11.8", + scalaVersion := "2.12.1", javacOptions ++= Seq("-source", "1.8", "-target", "1.8"), scalacOptions ++= Seq( "-encoding", "UTF-8", // yes, this is 2 args @@ -13,8 +13,7 @@ object Common { "-unchecked", "-Xlint", "-Yno-adapted-args", - "-Ywarn-numeric-widen", - "-Xfatal-warnings" + "-Ywarn-numeric-widen" ), resolvers ++= Seq( "scalaz-bintray" at "http://dl.bintray.com/scalaz/releases", diff --git a/project/build.properties b/project/build.properties index 27e88aa11..64317fdae 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=0.13.13 +sbt.version=0.13.15 From 4ff879c2ee1c9cc14dd644ab6a42281f452c4adf Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Tue, 18 Apr 2017 13:11:56 -0700 Subject: [PATCH 24/53] Updated with template-control on 2017-04-18T20:11:56.359Z /LICENSE: wrote /LICENSE **/build.sbt: scalaVersion := "2.12.2" **/build.sbt: libraryDependencies += "com.h2database" % "h2" % "1.4.192" --- build.sbt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index 8268d9188..08476437f 100644 --- a/build.sbt +++ b/build.sbt @@ -2,7 +2,7 @@ name := """play-isolated-slick""" version := "1.1-SNAPSHOT" -scalaVersion := "2.12.1" +scalaVersion := "2.12.2" lazy val flyway = (project in file("modules/flyway")) .enablePlugins(FlywayPlugin) @@ -23,7 +23,7 @@ lazy val root = (project in file(".")) TwirlKeys.templateImports += "com.example.user.User" libraryDependencies += guice -libraryDependencies += "com.h2database" % "h2" % "1.4.194" +libraryDependencies += "com.h2database" % "h2" % "1.4.192" // Automatic database migration available in testing fork in Test := true From d447cb924fe2b90be4687322f4fee365d058397d Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Tue, 18 Apr 2017 13:41:25 -0700 Subject: [PATCH 25/53] Updated with template-control on 2017-04-18T20:41:25.882Z /LICENSE: wrote /LICENSE **/build.sbt: libraryDependencies += "com.h2database" % "h2" % "1.4.194" --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 08476437f..ea4cfb5e8 100644 --- a/build.sbt +++ b/build.sbt @@ -23,7 +23,7 @@ lazy val root = (project in file(".")) TwirlKeys.templateImports += "com.example.user.User" libraryDependencies += guice -libraryDependencies += "com.h2database" % "h2" % "1.4.192" +libraryDependencies += "com.h2database" % "h2" % "1.4.194" // Automatic database migration available in testing fork in Test := true From 34ecd8179886bfae16463cc4748eeaa214976d9a Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Thu, 27 Apr 2017 18:46:59 -0700 Subject: [PATCH 26/53] Updated with template-control on 2017-04-28T01:46:59.056Z /LICENSE: wrote /LICENSE **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-M5") --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 82bb44090..c1ab215b2 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -14,4 +14,4 @@ addSbtPlugin("com.github.tototoshi" % "sbt-slick-codegen" % "1.2.0") libraryDependencies += "com.h2database" % "h2" % "1.4.192" // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-M4") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-M5") From 67d08b0c85ebf87b5805b72e734904de93c92623 Mon Sep 17 00:00:00 2001 From: Greg Methvin Date: Sun, 4 Jun 2017 00:24:22 +0200 Subject: [PATCH 27/53] Updated with template-control on 2017-06-03T16:01:09.617Z (#39) **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-RC2") --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index c1ab215b2..b4564e67c 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -14,4 +14,4 @@ addSbtPlugin("com.github.tototoshi" % "sbt-slick-codegen" % "1.2.0") libraryDependencies += "com.h2database" % "h2" % "1.4.192" // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-M5") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-RC2") From c3ae05a904e6799ab5ab4a0085c686f91cc29869 Mon Sep 17 00:00:00 2001 From: Greg Methvin Date: Fri, 23 Jun 2017 02:18:36 +0000 Subject: [PATCH 28/53] Updated with template-control on 2017-06-23T02:18:35.995Z **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0") --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index b4564e67c..4ce694ddb 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -14,4 +14,4 @@ addSbtPlugin("com.github.tototoshi" % "sbt-slick-codegen" % "1.2.0") libraryDependencies += "com.h2database" % "h2" % "1.4.192" // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-RC2") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0") From 04abb43bbb6868d95db4e8694fa0528cf3aaccd2 Mon Sep 17 00:00:00 2001 From: Greg Methvin Date: Fri, 7 Jul 2017 00:45:45 +0000 Subject: [PATCH 29/53] Updated with template-control on 2017-07-07T00:45:45.335Z **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.1") --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 4ce694ddb..183d125b7 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -14,4 +14,4 @@ addSbtPlugin("com.github.tototoshi" % "sbt-slick-codegen" % "1.2.0") libraryDependencies += "com.h2database" % "h2" % "1.4.192" // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.1") From 5ecf5dfe4ded1e903f6107a6ef0d9c53f4985e89 Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Wed, 19 Jul 2017 23:11:33 -0300 Subject: [PATCH 30/53] Updated with template-control on 2017-07-20T01:09:46.526Z (#43) **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.2") --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 183d125b7..167934c3e 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -14,4 +14,4 @@ addSbtPlugin("com.github.tototoshi" % "sbt-slick-codegen" % "1.2.0") libraryDependencies += "com.h2database" % "h2" % "1.4.192" // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.1") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.2") From 7c178594c2d8d06a373a8d9f2a8efcdadfb67009 Mon Sep 17 00:00:00 2001 From: Rich Dougherty Date: Sat, 12 Aug 2017 16:27:54 +1200 Subject: [PATCH 31/53] Updated with template-control on 2017-08-12T02:43:22.806Z (#45) **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.3") --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 167934c3e..3a24dabf3 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -14,4 +14,4 @@ addSbtPlugin("com.github.tototoshi" % "sbt-slick-codegen" % "1.2.0") libraryDependencies += "com.h2database" % "h2" % "1.4.192" // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.2") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.3") From e4a667c92ba20f1d75d99df8e8aee3400af65f70 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Mon, 4 Sep 2017 09:52:37 -0700 Subject: [PATCH 32/53] Update application.conf (#46) --- modules/slick/src/main/resources/application.conf | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/modules/slick/src/main/resources/application.conf b/modules/slick/src/main/resources/application.conf index 897532e3b..43eb52cc0 100644 --- a/modules/slick/src/main/resources/application.conf +++ b/modules/slick/src/main/resources/application.conf @@ -25,15 +25,4 @@ myapp = { } } -// The custom database dispatcher should match the number of threads allocated to Slick -// https://www.playframework.com/documentation/latest/ThreadPools -myapp.database-dispatcher { - type = Dispatcher - executor = "thread-pool-executor" - thread-pool-executor { - core-pool-size-min = 4 - core-pool-size-max = 4 - } -} - From 29a0378bab9658b46efc077baad63b399a2feb72 Mon Sep 17 00:00:00 2001 From: Greg Methvin Date: Thu, 14 Sep 2017 23:29:37 +0000 Subject: [PATCH 33/53] Updated with template-control on 2017-09-14T23:29:37.040Z **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.5") --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 3a24dabf3..8748c4bc6 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -14,4 +14,4 @@ addSbtPlugin("com.github.tototoshi" % "sbt-slick-codegen" % "1.2.0") libraryDependencies += "com.h2database" % "h2" % "1.4.192" // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.3") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.5") From 7d470c274b7deb161b376ef5d9f377e4bce80dbb Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Fri, 22 Dec 2017 11:35:25 -0200 Subject: [PATCH 34/53] Update Play to version 2.6.10 (#52) --- .travis.yml | 2 +- README.md | 21 ++++++++++--------- build.sbt | 10 ++++----- .../main/scala/com/example/user/UserDAO.scala | 2 +- modules/flyway/build.sbt | 4 +--- modules/slick/build.sbt | 6 +++--- project/Build.scala | 4 ++-- project/build.properties | 2 +- project/plugins.sbt | 10 +++++---- 9 files changed, 31 insertions(+), 30 deletions(-) diff --git a/.travis.yml b/.travis.yml index fa1d24130..e556a7292 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: scala scala: -- 2.12.1 +- 2.12.4 jdk: - oraclejdk8 script: diff --git a/README.md b/README.md index 7955d4a13..b61f8dadd 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,18 @@ # Play with Slick 3.1 -This project shows Play working with Slick. +[![Build Status](https://travis-ci.org/playframework/play-scala-isolated-slick-example.svg?branch=2.6.x)](https://travis-ci.org/playframework/play-scala-isolated-slick-example) -This project is configured to keep all the modules self-contained. +This project shows Play working with Slick. -* Slick is isolated from Play, not using play-slick. +This project is configured to keep all the modules self-contained. + +* Slick is isolated from Play, not using play-slick. * Database migration is done using [Flyway](https://flywaydb.org/), not Play Evolutions. * Slick's classes are auto-generated following database migration. ## Database Migration -``` +```bash sbt flyway/flywayMigrate ``` @@ -18,7 +20,7 @@ sbt flyway/flywayMigrate You will need to run the flywayMigrate task first, and then you will be able to generate tables using sbt-codegen. -``` +```bash sbt slickCodegen ``` @@ -26,7 +28,7 @@ sbt slickCodegen You can run functional tests against an in memory database and Slick easily with Play from a clean slate: -``` +```bash sbt clean flyway/flywayMigrate slickCodegen compile test ``` @@ -34,11 +36,10 @@ sbt clean flyway/flywayMigrate slickCodegen compile test To run the project, start up Play: -``` +```bash sbt run ``` +And that's it! -And that's it! - -Now go to [http://localhost:9000](http://localhost:9000), and you will see the list of users in the database. +Now go to , and you will see the list of users in the database. diff --git a/build.sbt b/build.sbt index ea4cfb5e8..70a3e07c7 100644 --- a/build.sbt +++ b/build.sbt @@ -2,7 +2,7 @@ name := """play-isolated-slick""" version := "1.1-SNAPSHOT" -scalaVersion := "2.12.2" +scalaVersion := "2.12.4" lazy val flyway = (project in file("modules/flyway")) .enablePlugins(FlywayPlugin) @@ -23,11 +23,11 @@ lazy val root = (project in file(".")) TwirlKeys.templateImports += "com.example.user.User" libraryDependencies += guice -libraryDependencies += "com.h2database" % "h2" % "1.4.194" +libraryDependencies += "com.h2database" % "h2" % "1.4.196" // Automatic database migration available in testing fork in Test := true -libraryDependencies += "org.flywaydb" % "flyway-core" % "4.1.2" % Test -libraryDependencies += "com.typesafe.play" %% "play-ahc-ws" % "2.6.0-M4" % Test -libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "3.0.0-M3" % Test +libraryDependencies += "org.flywaydb" % "flyway-core" % "5.0.3" +libraryDependencies += "com.typesafe.play" %% "play-ahc-ws" % "2.6.10" % Test +libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "3.1.2" % Test diff --git a/modules/api/src/main/scala/com/example/user/UserDAO.scala b/modules/api/src/main/scala/com/example/user/UserDAO.scala index 85309bca8..6bc67e021 100644 --- a/modules/api/src/main/scala/com/example/user/UserDAO.scala +++ b/modules/api/src/main/scala/com/example/user/UserDAO.scala @@ -2,7 +2,7 @@ package com.example.user import org.joda.time.DateTime -import scala.concurrent.{ExecutionContext, Future} +import scala.concurrent.Future /** * An implementation dependent DAO. This could be implemented by Slick, Cassandra, or a REST API. diff --git a/modules/flyway/build.sbt b/modules/flyway/build.sbt index 83a7fb062..0bcbf9b45 100644 --- a/modules/flyway/build.sbt +++ b/modules/flyway/build.sbt @@ -1,5 +1,3 @@ - - // Database Migrations: // run with "sbt flywayMigrate" // http://flywaydb.org/getstarted/firststeps/sbt.html @@ -8,7 +6,7 @@ //$ export DB_DEFAULT_USER="sa" //$ export DB_DEFAULT_PASSWORD="" -libraryDependencies += "org.flywaydb" % "flyway-core" % "4.0" +libraryDependencies += "org.flywaydb" % "flyway-core" % "5.0.3" lazy val databaseUrl = sys.env.getOrElse("DB_DEFAULT_URL", "jdbc:h2:./test") lazy val databaseUser = sys.env.getOrElse("DB_DEFAULT_USER", "sa") diff --git a/modules/slick/build.sbt b/modules/slick/build.sbt index 2cf9168f8..cdc6b32db 100644 --- a/modules/slick/build.sbt +++ b/modules/slick/build.sbt @@ -2,9 +2,9 @@ import slick.codegen.SourceCodeGenerator import slick.{ model => m } libraryDependencies ++= Seq( - "com.zaxxer" % "HikariCP" % "2.6.1", - "com.typesafe.slick" %% "slick" % "3.2.0", - "com.typesafe.slick" %% "slick-hikaricp" % "3.2.0", + "com.zaxxer" % "HikariCP" % "2.7.4", + "com.typesafe.slick" %% "slick" % "3.2.1", + "com.typesafe.slick" %% "slick-hikaricp" % "3.2.1", "com.github.tototoshi" %% "slick-joda-mapper" % "2.3.0" ) diff --git a/project/Build.scala b/project/Build.scala index 8cd71b9ba..1ca28a93a 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -4,7 +4,7 @@ import sbt.{Resolver, _} object Common { def projectSettings = Seq( - scalaVersion := "2.12.1", + scalaVersion := "2.12.4", javacOptions ++= Seq("-source", "1.8", "-target", "1.8"), scalacOptions ++= Seq( "-encoding", "UTF-8", // yes, this is 2 args @@ -22,7 +22,7 @@ object Common { libraryDependencies ++= Seq( "javax.inject" % "javax.inject" % "1", "joda-time" % "joda-time" % "2.9.9", - "org.joda" % "joda-convert" % "1.8.1", + "org.joda" % "joda-convert" % "1.9.2", "com.google.inject" % "guice" % "4.1.0" ), scalacOptions in Test ++= Seq("-Yrangepos") diff --git a/project/build.properties b/project/build.properties index 64317fdae..c091b86ca 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=0.13.15 +sbt.version=0.13.16 diff --git a/project/plugins.sbt b/project/plugins.sbt index 8748c4bc6..6e3011b08 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -4,14 +4,16 @@ resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repos resolvers += "Flyway" at "https://flywaydb.org/repo" +resolvers += "Flyway" at "https://davidmweber.github.io/flyway-sbt.repo" + // Database migration -addSbtPlugin("org.flywaydb" % "flyway-sbt" % "4.0") +addSbtPlugin("org.flywaydb" % "flyway-sbt" % "4.2.0") // Slick code generation // https://github.com/tototoshi/sbt-slick-codegen -addSbtPlugin("com.github.tototoshi" % "sbt-slick-codegen" % "1.2.0") +addSbtPlugin("com.github.tototoshi" % "sbt-slick-codegen" % "1.2.1") -libraryDependencies += "com.h2database" % "h2" % "1.4.192" +libraryDependencies += "com.h2database" % "h2" % "1.4.196" // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.5") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.10") From 743c82b80878eb5be9f52e172adc4b546112865e Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Thu, 11 Jan 2018 17:15:15 -0200 Subject: [PATCH 35/53] Fix cross build to sbt 1.1.0 (#55) --- build.sbt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.sbt b/build.sbt index 70a3e07c7..4ee1f8336 100644 --- a/build.sbt +++ b/build.sbt @@ -4,6 +4,8 @@ version := "1.1-SNAPSHOT" scalaVersion := "2.12.4" +crossScalaVersions := Seq("2.11.12", "2.12.4") + lazy val flyway = (project in file("modules/flyway")) .enablePlugins(FlywayPlugin) From f58f4c8dce05d3c73983478cea658a6e42d733cd Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Wed, 14 Mar 2018 17:32:19 -0300 Subject: [PATCH 36/53] Upgrade branch 2.6.x using TemplateControl (#58) * Updated with template-control on 2018-03-02T18:56:28.184Z **/build.properties: sbt.version=1.1.1 **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.12") * Update sbt-slick-codegen --- project/build.properties | 2 +- project/plugins.sbt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/project/build.properties b/project/build.properties index c091b86ca..31334bbd3 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=0.13.16 +sbt.version=1.1.1 diff --git a/project/plugins.sbt b/project/plugins.sbt index 6e3011b08..622f1eeb6 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -11,9 +11,9 @@ addSbtPlugin("org.flywaydb" % "flyway-sbt" % "4.2.0") // Slick code generation // https://github.com/tototoshi/sbt-slick-codegen -addSbtPlugin("com.github.tototoshi" % "sbt-slick-codegen" % "1.2.1") +addSbtPlugin("com.github.tototoshi" % "sbt-slick-codegen" % "1.3.0") libraryDependencies += "com.h2database" % "h2" % "1.4.196" // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.10") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.12") From 72ef89b79a4c24e42369d37d829118790b0ad822 Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Sat, 7 Apr 2018 10:51:30 -0300 Subject: [PATCH 37/53] Updated with template-control on 2018-04-06T19:34:57.999Z (#60) **/build.properties: sbt.version=1.1.2 **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.13") --- project/build.properties | 2 +- project/plugins.sbt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/project/build.properties b/project/build.properties index 31334bbd3..05313438a 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.1.1 +sbt.version=1.1.2 diff --git a/project/plugins.sbt b/project/plugins.sbt index 622f1eeb6..1668d8620 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -16,4 +16,4 @@ addSbtPlugin("com.github.tototoshi" % "sbt-slick-codegen" % "1.3.0") libraryDependencies += "com.h2database" % "h2" % "1.4.196" // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.12") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.13") From 151e3bcd25050dab70ca249cd28adf9e0bdc09de Mon Sep 17 00:00:00 2001 From: Rich Dougherty Date: Tue, 29 May 2018 03:13:54 +1200 Subject: [PATCH 38/53] Updated with template-control on 2018-05-27T23:54:09.691Z (#62) **build.sbt: scalaVersion := "2.12.6" **build.sbt: libraryDependencies += "com.h2database" % "h2" % "1.4.197" **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.15") --- build.sbt | 4 ++-- project/plugins.sbt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.sbt b/build.sbt index 4ee1f8336..73233022c 100644 --- a/build.sbt +++ b/build.sbt @@ -2,7 +2,7 @@ name := """play-isolated-slick""" version := "1.1-SNAPSHOT" -scalaVersion := "2.12.4" +scalaVersion := "2.12.6" crossScalaVersions := Seq("2.11.12", "2.12.4") @@ -25,7 +25,7 @@ lazy val root = (project in file(".")) TwirlKeys.templateImports += "com.example.user.User" libraryDependencies += guice -libraryDependencies += "com.h2database" % "h2" % "1.4.196" +libraryDependencies += "com.h2database" % "h2" % "1.4.197" // Automatic database migration available in testing fork in Test := true diff --git a/project/plugins.sbt b/project/plugins.sbt index 1668d8620..5835021a7 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -16,4 +16,4 @@ addSbtPlugin("com.github.tototoshi" % "sbt-slick-codegen" % "1.3.0") libraryDependencies += "com.h2database" % "h2" % "1.4.196" // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.13") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.15") From 92303013832c36654d20cf4854d74a94b10190fb Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Mon, 18 Jun 2018 12:59:16 -0400 Subject: [PATCH 39/53] Upgrade branch 2.6.x using TemplateControl (#63) * Updated with template-control on 2018-06-08T21:15:48.913Z **/build.properties: sbt.version=1.1.6 * Add Java 10 and 11 to Travis build * Remove Scala 2.11 from Travis build * Update other dependencies --- .travis.yml | 25 ++++++++++++++++++++++++- build.sbt | 6 +++--- modules/slick/build.sbt | 6 +++--- project/Build.scala | 2 +- project/build.properties | 2 +- 5 files changed, 32 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index e556a7292..3e651d5d0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,11 @@ language: scala scala: -- 2.12.4 +- 2.12.6 jdk: - oraclejdk8 +- oraclejdk9 +- oraclejdk10 +- oraclejdk11 script: sbt ++$TRAVIS_SCALA_VERSION clean flyway/flywayMigrate slickCodegen compile test cache: @@ -12,6 +15,26 @@ before_cache: - rm -rf $HOME/.ivy2/cache/com.typesafe.play/* - rm -rf $HOME/.ivy2/cache/scala_*/sbt_*/com.typesafe.play/* - find $HOME/.ivy2/cache -name "ivydata-*.properties" -print0 | xargs -n10 -0 rm + +# Exclude some combinations from build matrix. See: +# https://docs.travis-ci.com/user/customizing-the-build/#Build-Matrix +matrix: + exclude: + - scala: 2.11.12 + jdk: oraclejdk9 + - scala: 2.11.12 + jdk: oraclejdk10 + - scala: 2.11.12 + jdk: oraclejdk11 + allow_failures: + # We should allow failures here since Java 11 removed some modules including + # java.xml.bind which we are adding when running with Java 9+. For more details + # see http://openjdk.java.net/jeps/320 + # + # Play already has a fix for that, but it needs to be backported and released + # for 2.6.x: https://github.com/playframework/playframework/pull/8382 + - jdk: oraclejdk11 + notifications: slack: secure: bHNB8qvP8Da51PgA8tHiM4ePhusZlLIFXNItkyo0HuS1L1I06BRKOe2tFyU6dy8hz8cIEh2lFbQ5WIhjXFXPxqez6SdacFtInX4BRj7Q6QJP7Q+79h3OHQAz6niYWaGZZ1BtMucSlZcXYiAky5Tsj3cOesUM18T/HLrSfg8UjJHflSyT3wWnYcuRIHuC/YPjNqUn7Brrf/yaRClR80ffgx7eq/KF912bupsj/17eF/rPs65B2ux6M0yeSdUiXLIDNSm9Dh18ZTb+Hq4RjryT9EySO4X7rsuauqPWbA2OM0EFT3+0QLxD+y8EEAZGqGvEYnoEfikuIqCCr5v5Zsayg3J7mb5hgpqTZQWazmJFQ/+m76vP5YOP8/xI2+gbCv95kYHmkSItHza/VZhwP06pQyVMs2kXVoejwRm27M+WG36r+2bjU23o7R/EmyLMjcOdvXF/+Czi3KzMCDyX8AxVl9VOilvGlvTitwkmba1GM2pST9Pr81zh6ktyBsfx3f/DstalgkaVB+ftFrxRsawwlxKy6Xg8hSIR/ifsWdIhTucqMwjvUa7PmCtDZcIiXTgQVBBTVHtHmzuM07zo/0jTaQ/sP0Hlj4YgHz87/1lwoTTEVErLcVyl1FG30vrt8rophMk/cl8UGmw17f5XRh9jVfGwokh5UuUS1/4RcUvBqlM= diff --git a/build.sbt b/build.sbt index 73233022c..998917672 100644 --- a/build.sbt +++ b/build.sbt @@ -4,7 +4,7 @@ version := "1.1-SNAPSHOT" scalaVersion := "2.12.6" -crossScalaVersions := Seq("2.11.12", "2.12.4") +crossScalaVersions := Seq("2.11.12", "2.12.6") lazy val flyway = (project in file("modules/flyway")) .enablePlugins(FlywayPlugin) @@ -29,7 +29,7 @@ libraryDependencies += "com.h2database" % "h2" % "1.4.197" // Automatic database migration available in testing fork in Test := true -libraryDependencies += "org.flywaydb" % "flyway-core" % "5.0.3" -libraryDependencies += "com.typesafe.play" %% "play-ahc-ws" % "2.6.10" % Test +libraryDependencies += "org.flywaydb" % "flyway-core" % "5.1.1" +libraryDependencies += "com.typesafe.play" %% "play-ahc-ws" % "2.6.15" % Test libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "3.1.2" % Test diff --git a/modules/slick/build.sbt b/modules/slick/build.sbt index cdc6b32db..bac898945 100644 --- a/modules/slick/build.sbt +++ b/modules/slick/build.sbt @@ -2,9 +2,9 @@ import slick.codegen.SourceCodeGenerator import slick.{ model => m } libraryDependencies ++= Seq( - "com.zaxxer" % "HikariCP" % "2.7.4", - "com.typesafe.slick" %% "slick" % "3.2.1", - "com.typesafe.slick" %% "slick-hikaricp" % "3.2.1", + "com.zaxxer" % "HikariCP" % "2.7.9", + "com.typesafe.slick" %% "slick" % "3.2.3", + "com.typesafe.slick" %% "slick-hikaricp" % "3.2.3", "com.github.tototoshi" %% "slick-joda-mapper" % "2.3.0" ) diff --git a/project/Build.scala b/project/Build.scala index 1ca28a93a..9e5f7fba3 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -4,7 +4,7 @@ import sbt.{Resolver, _} object Common { def projectSettings = Seq( - scalaVersion := "2.12.4", + scalaVersion := "2.12.6", javacOptions ++= Seq("-source", "1.8", "-target", "1.8"), scalacOptions ++= Seq( "-encoding", "UTF-8", // yes, this is 2 args diff --git a/project/build.properties b/project/build.properties index 05313438a..d6e35076c 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.1.2 +sbt.version=1.1.6 From 334ed40616144068d21b3ad4642bdf4bdcfdcb60 Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Mon, 16 Jul 2018 15:32:14 -0400 Subject: [PATCH 40/53] Updated with template-control on 2018-07-16T18:38:45.677Z (#64) **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.16") --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 5835021a7..bdd1019b4 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -16,4 +16,4 @@ addSbtPlugin("com.github.tototoshi" % "sbt-slick-codegen" % "1.3.0") libraryDependencies += "com.h2database" % "h2" % "1.4.196" // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.15") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.16") From e457acdd91bed817ac854efe451324876e5ae71d Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Thu, 19 Jul 2018 12:26:27 -0400 Subject: [PATCH 41/53] Updated with template-control on 2018-07-19T01:58:56.035Z (#65) **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.17") --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index bdd1019b4..17536baee 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -16,4 +16,4 @@ addSbtPlugin("com.github.tototoshi" % "sbt-slick-codegen" % "1.3.0") libraryDependencies += "com.h2database" % "h2" % "1.4.196" // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.16") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.17") From 9f6e682fc3ba01e4124ebb0d0a07c7bf7004874c Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Mon, 20 Aug 2018 16:35:05 -0400 Subject: [PATCH 42/53] Updated with template-control on 2018-08-20T19:30:29.032Z (#67) **/build.properties: sbt.version=1.2.1 **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.18") --- project/build.properties | 2 +- project/plugins.sbt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/project/build.properties b/project/build.properties index d6e35076c..5620cc502 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.1.6 +sbt.version=1.2.1 diff --git a/project/plugins.sbt b/project/plugins.sbt index 17536baee..32d9bf228 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -16,4 +16,4 @@ addSbtPlugin("com.github.tototoshi" % "sbt-slick-codegen" % "1.3.0") libraryDependencies += "com.h2database" % "h2" % "1.4.196" // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.17") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.18") From 8646ed379e910231af64dde946d7feb2e783aa39 Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Tue, 11 Sep 2018 18:09:04 -0400 Subject: [PATCH 43/53] Updated with template-control on 2018-09-11T20:14:50.342Z (#68) **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.19") --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 32d9bf228..70388cb20 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -16,4 +16,4 @@ addSbtPlugin("com.github.tototoshi" % "sbt-slick-codegen" % "1.3.0") libraryDependencies += "com.h2database" % "h2" % "1.4.196" // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.18") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.19") From a1bd627e1eaa7335e5f5d1a518f35ab27928647e Mon Sep 17 00:00:00 2001 From: Renato Cavalcanti Date: Mon, 8 Oct 2018 21:11:41 +0200 Subject: [PATCH 44/53] Updated with template-control on 2018-10-08T19:11:41.295Z **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.20") --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 70388cb20..a4f3df945 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -16,4 +16,4 @@ addSbtPlugin("com.github.tototoshi" % "sbt-slick-codegen" % "1.3.0") libraryDependencies += "com.h2database" % "h2" % "1.4.196" // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.19") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.20") From e12af4d80c20cda45b4bbca0fb2fa99e2623a2b2 Mon Sep 17 00:00:00 2001 From: Renato Cavalcanti Date: Thu, 29 Nov 2018 16:50:43 +0100 Subject: [PATCH 45/53] Updated with template-control on 2018-11-29T15:50:43.213Z /.mergify.yml: wrote /.mergify.yml **build.sbt: scalaVersion := "2.12.7" --- .mergify.yml | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++ build.sbt | 2 +- 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 .mergify.yml diff --git a/.mergify.yml b/.mergify.yml new file mode 100644 index 000000000..4a37a16dd --- /dev/null +++ b/.mergify.yml @@ -0,0 +1,57 @@ +pull_request_rules: + - name: automatic merge on CI success require review + conditions: + - status-success=continuous-integration/travis-ci/pr + - "#approved-reviews-by>=1" + - "#changes-requested-reviews-by=0" + - label=merge-when-green + - label!=block-merge + actions: + merge: + method: squash + strict: smart + + - name: automatic merge on CI success for TemplateControl + conditions: + - status-success=continuous-integration/travis-ci/pr + - label=merge-when-green + - label=template-control + - label!=block-merge + actions: + merge: + method: squash + strict: smart + + # delete any branch when already merged + # doesn't matter if marked with labels or not + - name: delete branch after merge + conditions: + - merged + actions: + delete_head_branch: {} + + # delete 'merge-when-green' label if present and merged + - name: remove label after merge + conditions: + - merged + - label=merge-when-green + actions: + label: + remove: [merge-when-green] + + # delete 'template-control' label if present and merged + - name: remove label after merge + conditions: + - merged + - label=template-control + actions: + label: + remove: [template-control] + + - name: auto add wip + conditions: + # match a few flavours of wip + - title~=^(\[wip\]( |:) |\[WIP\]( |:) |wip( |:) |WIP( |:)).* + actions: + label: + add: ["block-merge"] \ No newline at end of file diff --git a/build.sbt b/build.sbt index 998917672..7d27ae7b9 100644 --- a/build.sbt +++ b/build.sbt @@ -2,7 +2,7 @@ name := """play-isolated-slick""" version := "1.1-SNAPSHOT" -scalaVersion := "2.12.6" +scalaVersion := "2.12.7" crossScalaVersions := Seq("2.11.12", "2.12.6") From 7765c53d2e343a1c5647cb93ce47f2a43cc51aa3 Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Thu, 6 Dec 2018 09:42:04 -0500 Subject: [PATCH 46/53] Remove JDK 9 & 10 --- .travis.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3e651d5d0..81c72d7a4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,6 @@ scala: - 2.12.6 jdk: - oraclejdk8 -- oraclejdk9 -- oraclejdk10 - oraclejdk11 script: sbt ++$TRAVIS_SCALA_VERSION clean flyway/flywayMigrate slickCodegen compile test @@ -19,11 +17,8 @@ before_cache: # Exclude some combinations from build matrix. See: # https://docs.travis-ci.com/user/customizing-the-build/#Build-Matrix matrix: + fast_finish: true exclude: - - scala: 2.11.12 - jdk: oraclejdk9 - - scala: 2.11.12 - jdk: oraclejdk10 - scala: 2.11.12 jdk: oraclejdk11 allow_failures: From 7f572bd0a89de9f379446fff2f3bedc9acc6e3b0 Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Fri, 4 Jan 2019 16:23:28 -0500 Subject: [PATCH 47/53] Upgrade branch 2.6.x using TemplateControl (#73) * Updated with template-control on 2019-01-04T17:13:23.991Z /.travis.yml: wrote /.travis.yml **build.sbt: scalaVersion := "2.12.8" **/build.properties: sbt.version=1.2.8 * Add test scripts --- .travis.yml | 67 ++++++++++++++++++++++++---------------- build.sbt | 2 +- project/build.properties | 2 +- scripts/script-helper | 13 ++++++++ scripts/test-gradle | 13 ++++++++ scripts/test-sbt | 8 +++++ 6 files changed, 77 insertions(+), 28 deletions(-) create mode 100755 scripts/script-helper create mode 100755 scripts/test-gradle create mode 100755 scripts/test-sbt diff --git a/.travis.yml b/.travis.yml index 81c72d7a4..1e8c0e7c4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,35 +1,50 @@ language: scala scala: -- 2.12.6 -jdk: -- oraclejdk8 -- oraclejdk11 -script: - sbt ++$TRAVIS_SCALA_VERSION clean flyway/flywayMigrate slickCodegen compile test -cache: - directories: - - "$HOME/.ivy2/cache" -before_cache: -- rm -rf $HOME/.ivy2/cache/com.typesafe.play/* -- rm -rf $HOME/.ivy2/cache/scala_*/sbt_*/com.typesafe.play/* -- find $HOME/.ivy2/cache -name "ivydata-*.properties" -print0 | xargs -n10 -0 rm + - 2.12.8 + +before_install: + - curl -sL https://github.com/shyiko/jabba/raw/master/install.sh | bash && . ~/.jabba/jabba.sh + +env: + global: + - JABBA_HOME=$HOME/.jabba + matrix: + # There is no concise way to specify multi-dimensional build matrix: + # https://github.com/travis-ci/travis-ci/issues/1519 + - SCRIPT=scripts/test-sbt TRAVIS_JDK=adopt@1.8.192-12 + - SCRIPT=scripts/test-sbt TRAVIS_JDK=adopt@1.11.0-1 + - SCRIPT=scripts/test-gradle TRAVIS_JDK=adopt@1.8.192-12 + - SCRIPT=scripts/test-gradle TRAVIS_JDK=adopt@1.11.0-1 # Exclude some combinations from build matrix. See: # https://docs.travis-ci.com/user/customizing-the-build/#Build-Matrix matrix: fast_finish: true - exclude: - - scala: 2.11.12 - jdk: oraclejdk11 allow_failures: - # We should allow failures here since Java 11 removed some modules including - # java.xml.bind which we are adding when running with Java 9+. For more details - # see http://openjdk.java.net/jeps/320 - # - # Play already has a fix for that, but it needs to be backported and released - # for 2.6.x: https://github.com/playframework/playframework/pull/8382 - - jdk: oraclejdk11 + # Current release of Gradle still does not supports Play 2.7.x releases + # As soon as there is a release of Gradle that fixes that, we can then + # remove this allowed failure. + - env: SCRIPT=scripts/test-gradle TRAVIS_JDK=adopt@1.8.192-12 + - env: SCRIPT=scripts/test-gradle TRAVIS_JDK=adopt@1.11.0-1 + # Java 11 is still not fully supported. It is good that we are already + # testing our sample applications to better discover possible problems + # but we can allow failures here too. + - env: SCRIPT=scripts/test-sbt TRAVIS_JDK=adopt@1.11.0-1 + +install: + - $JABBA_HOME/bin/jabba install $TRAVIS_JDK + - unset _JAVA_OPTIONS + - export JAVA_HOME="$JABBA_HOME/jdk/$TRAVIS_JDK" && export PATH="$JAVA_HOME/bin:$PATH" && java -Xmx32m -version -notifications: - slack: - secure: bHNB8qvP8Da51PgA8tHiM4ePhusZlLIFXNItkyo0HuS1L1I06BRKOe2tFyU6dy8hz8cIEh2lFbQ5WIhjXFXPxqez6SdacFtInX4BRj7Q6QJP7Q+79h3OHQAz6niYWaGZZ1BtMucSlZcXYiAky5Tsj3cOesUM18T/HLrSfg8UjJHflSyT3wWnYcuRIHuC/YPjNqUn7Brrf/yaRClR80ffgx7eq/KF912bupsj/17eF/rPs65B2ux6M0yeSdUiXLIDNSm9Dh18ZTb+Hq4RjryT9EySO4X7rsuauqPWbA2OM0EFT3+0QLxD+y8EEAZGqGvEYnoEfikuIqCCr5v5Zsayg3J7mb5hgpqTZQWazmJFQ/+m76vP5YOP8/xI2+gbCv95kYHmkSItHza/VZhwP06pQyVMs2kXVoejwRm27M+WG36r+2bjU23o7R/EmyLMjcOdvXF/+Czi3KzMCDyX8AxVl9VOilvGlvTitwkmba1GM2pST9Pr81zh6ktyBsfx3f/DstalgkaVB+ftFrxRsawwlxKy6Xg8hSIR/ifsWdIhTucqMwjvUa7PmCtDZcIiXTgQVBBTVHtHmzuM07zo/0jTaQ/sP0Hlj4YgHz87/1lwoTTEVErLcVyl1FG30vrt8rophMk/cl8UGmw17f5XRh9jVfGwokh5UuUS1/4RcUvBqlM= +script: + - $SCRIPT + +before_cache: + - find $HOME/.ivy2 -name "ivydata-*.properties" -delete + - find $HOME/.sbt -name "*.lock" -delete + +cache: + directories: + - "$HOME/.ivy2/cache" + - "$HOME/.gradle/caches" + - "$HOME/.jabba/jdk" diff --git a/build.sbt b/build.sbt index 7d27ae7b9..1b61cfcd1 100644 --- a/build.sbt +++ b/build.sbt @@ -2,7 +2,7 @@ name := """play-isolated-slick""" version := "1.1-SNAPSHOT" -scalaVersion := "2.12.7" +scalaVersion := "2.12.8" crossScalaVersions := Seq("2.11.12", "2.12.6") diff --git a/project/build.properties b/project/build.properties index 5620cc502..c0bab0494 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.2.1 +sbt.version=1.2.8 diff --git a/scripts/script-helper b/scripts/script-helper new file mode 100755 index 000000000..9a2faa643 --- /dev/null +++ b/scripts/script-helper @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +set -e +set -o pipefail + +java_version=$(java -version 2>&1 | java -version 2>&1 | awk -F '"' '/version/ {print $2}') + +if [[ $java_version = 1.8* ]] ; then + echo "The build is using Java 8 ($java_version). No addional JVM params needed." +else + echo "The build is using Java 9+ ($java_version). We need additional JVM parameters" + export _JAVA_OPTIONS="$_JAVA_OPTIONS --add-modules=java.xml.bind" +fi diff --git a/scripts/test-gradle b/scripts/test-gradle new file mode 100755 index 000000000..84a051a20 --- /dev/null +++ b/scripts/test-gradle @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +. "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/script-helper" + +# Using cut because TRAVIS_SCALA_VERSION is the full Scala +# version (for example 2.12.4), but Gradle expects just the +# binary version (for example 2.12) +scala_binary_version=$(echo $TRAVIS_SCALA_VERSION | cut -c1-4) + +echo "+------------------------------+" +echo "| Executing tests using Gradle |" +echo "+------------------------------+" +./gradlew -Dscala.binary.version=$scala_binary_version check -i --stacktrace diff --git a/scripts/test-sbt b/scripts/test-sbt new file mode 100755 index 000000000..95bcad236 --- /dev/null +++ b/scripts/test-sbt @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +. "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/script-helper" + +echo "+----------------------------+" +echo "| Executing tests using sbt |" +echo "+----------------------------+" +sbt ++$TRAVIS_SCALA_VERSION clean flyway/flywayMigrate slickCodegen compile test From 7f245ad467c5bbb1280f523ac855d1946a11c452 Mon Sep 17 00:00:00 2001 From: Renato Cavalcanti Date: Tue, 8 Jan 2019 16:54:34 +0100 Subject: [PATCH 48/53] Updated with template-control on 2019-01-08T14:44:41.929Z (#78) **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.21") --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index a4f3df945..f131bb251 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -16,4 +16,4 @@ addSbtPlugin("com.github.tototoshi" % "sbt-slick-codegen" % "1.3.0") libraryDependencies += "com.h2database" % "h2" % "1.4.196" // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.20") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.21") From 87756226c0f2c92b50f5166cc2b94c2b1516e8c0 Mon Sep 17 00:00:00 2001 From: Renato Cavalcanti Date: Wed, 16 Jan 2019 13:08:46 +0100 Subject: [PATCH 49/53] Updated with template-control on 2019-01-16T12:08:45.882Z /LICENSE: wrote /LICENSE /NOTICE: wrote /NOTICE /.mergify.yml: wrote /.mergify.yml --- .mergify.yml | 10 ++--- LICENSE | 119 ++++++++++++++++++++++++++++++++++++++++++++++++--- NOTICE | 8 ++++ 3 files changed, 125 insertions(+), 12 deletions(-) create mode 100644 NOTICE diff --git a/.mergify.yml b/.mergify.yml index 4a37a16dd..3549efd41 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -22,16 +22,13 @@ pull_request_rules: method: squash strict: smart - # delete any branch when already merged - # doesn't matter if marked with labels or not - name: delete branch after merge conditions: - merged actions: delete_head_branch: {} - # delete 'merge-when-green' label if present and merged - - name: remove label after merge + - name: remove merge-when-green label after merge conditions: - merged - label=merge-when-green @@ -39,8 +36,7 @@ pull_request_rules: label: remove: [merge-when-green] - # delete 'template-control' label if present and merged - - name: remove label after merge + - name: remove template-control label after merge conditions: - merged - label=template-control @@ -54,4 +50,4 @@ pull_request_rules: - title~=^(\[wip\]( |:) |\[WIP\]( |:) |wip( |:) |WIP( |:)).* actions: label: - add: ["block-merge"] \ No newline at end of file + add: ["block-merge"] diff --git a/LICENSE b/LICENSE index b018ae2bc..670154e35 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,116 @@ -License -------- -Written in 2016 by Lightbend +CC0 1.0 Universal -To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty. +Statement of Purpose -You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see . +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/NOTICE b/NOTICE new file mode 100644 index 000000000..6d6c034d3 --- /dev/null +++ b/NOTICE @@ -0,0 +1,8 @@ +Written by Lightbend + +To the extent possible under law, the author(s) have dedicated all copyright and +related and neighboring rights to this software to the public domain worldwide. +This software is distributed without any warranty. + +You should have received a copy of the CC0 Public Domain Dedication along with +this software. If not, see . From 8375c3c715d9289694c88f138041d48a049e2128 Mon Sep 17 00:00:00 2001 From: Renato Cavalcanti Date: Thu, 17 Jan 2019 16:10:58 +0100 Subject: [PATCH 50/53] Updated with template-control on 2019-01-17T15:10:58.033Z /.mergify.yml: wrote /.mergify.yml --- .mergify.yml | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/.mergify.yml b/.mergify.yml index 3549efd41..b215a7709 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -4,7 +4,6 @@ pull_request_rules: - status-success=continuous-integration/travis-ci/pr - "#approved-reviews-by>=1" - "#changes-requested-reviews-by=0" - - label=merge-when-green - label!=block-merge actions: merge: @@ -15,7 +14,6 @@ pull_request_rules: conditions: - status-success=continuous-integration/travis-ci/pr - label=merge-when-green - - label=template-control - label!=block-merge actions: merge: @@ -35,19 +33,3 @@ pull_request_rules: actions: label: remove: [merge-when-green] - - - name: remove template-control label after merge - conditions: - - merged - - label=template-control - actions: - label: - remove: [template-control] - - - name: auto add wip - conditions: - # match a few flavours of wip - - title~=^(\[wip\]( |:) |\[WIP\]( |:) |wip( |:) |WIP( |:)).* - actions: - label: - add: ["block-merge"] From b4d4e0768f7d3536286dc75bb5c0a394a4a99681 Mon Sep 17 00:00:00 2001 From: Renato Cavalcanti Date: Fri, 1 Feb 2019 11:40:33 +0100 Subject: [PATCH 51/53] Updated with template-control on 2019-02-01T10:40:33.042Z /.mergify.yml: wrote /.mergify.yml --- .mergify.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.mergify.yml b/.mergify.yml index b215a7709..fbbe4380f 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -25,11 +25,3 @@ pull_request_rules: - merged actions: delete_head_branch: {} - - - name: remove merge-when-green label after merge - conditions: - - merged - - label=merge-when-green - actions: - label: - remove: [merge-when-green] From c4098857eeac40613ea7f2df67ce91bcca83076a Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Thu, 14 Feb 2019 00:39:30 -0500 Subject: [PATCH 52/53] Updated with template-control on 2019-02-13T20:25:39.661Z (#84) /.mergify.yml: wrote /.mergify.yml --- .mergify.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.mergify.yml b/.mergify.yml index fbbe4380f..32f8689ae 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -1,7 +1,7 @@ pull_request_rules: - name: automatic merge on CI success require review conditions: - - status-success=continuous-integration/travis-ci/pr + - status-success=Travis CI - Pull Request - "#approved-reviews-by>=1" - "#changes-requested-reviews-by=0" - label!=block-merge @@ -12,7 +12,7 @@ pull_request_rules: - name: automatic merge on CI success for TemplateControl conditions: - - status-success=continuous-integration/travis-ci/pr + - status-success=Travis CI - Pull Request - label=merge-when-green - label!=block-merge actions: From b17d69823a2cc48d49246216638facff2e2b5ff3 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 23 Apr 2019 15:49:57 +0100 Subject: [PATCH 53/53] Nest play-scala-isolated-slick-example --- .../.gitignore | 0 .../.mergify.yml | 0 .../.travis.yml | 0 .../LICENSE | 0 NOTICE => play-scala-isolated-slick-example/NOTICE | 0 .../README.md | 0 .../app}/Module.scala | 0 .../app}/controllers/HomeController.scala | 0 .../app}/views/index.scala.html | 0 .../app}/views/main.scala.html | 0 .../build.sbt | 0 .../conf}/logback.xml | 0 .../conf}/routes | 0 .../src/main/scala/com/example/user/UserDAO.scala | 0 .../modules}/flyway/build.sbt | 0 .../V20150409112518__create_users_table.sql | 0 .../db/migration/V20150409131208__add_user.sql | 0 .../modules}/slick/build.sbt | 0 .../slick/src/main/resources/application.conf | 0 .../scala/com/example/user/slick/SlickUserDAO.scala | 0 .../project}/Build.scala | 0 .../project}/build.properties | 0 .../project}/plugins.sbt | 0 .../public}/images/favicon.png | Bin .../public}/javascripts/hello.js | 0 .../public}/stylesheets/main.css | 0 .../scripts}/script-helper | 0 .../scripts}/test-gradle | 0 .../scripts}/test-sbt | 0 .../test}/controller/FunctionalSpec.scala | 0 .../test}/controller/MyApplicationFactory.scala | 0 31 files changed, 0 insertions(+), 0 deletions(-) rename .gitignore => play-scala-isolated-slick-example/.gitignore (100%) rename .mergify.yml => play-scala-isolated-slick-example/.mergify.yml (100%) rename .travis.yml => play-scala-isolated-slick-example/.travis.yml (100%) rename LICENSE => play-scala-isolated-slick-example/LICENSE (100%) rename NOTICE => play-scala-isolated-slick-example/NOTICE (100%) rename README.md => play-scala-isolated-slick-example/README.md (100%) rename {app => play-scala-isolated-slick-example/app}/Module.scala (100%) rename {app => play-scala-isolated-slick-example/app}/controllers/HomeController.scala (100%) rename {app => play-scala-isolated-slick-example/app}/views/index.scala.html (100%) rename {app => play-scala-isolated-slick-example/app}/views/main.scala.html (100%) rename build.sbt => play-scala-isolated-slick-example/build.sbt (100%) rename {conf => play-scala-isolated-slick-example/conf}/logback.xml (100%) rename {conf => play-scala-isolated-slick-example/conf}/routes (100%) rename {modules => play-scala-isolated-slick-example/modules}/api/src/main/scala/com/example/user/UserDAO.scala (100%) rename {modules => play-scala-isolated-slick-example/modules}/flyway/build.sbt (100%) rename {modules => play-scala-isolated-slick-example/modules}/flyway/src/main/resources/db/migration/V20150409112518__create_users_table.sql (100%) rename {modules => play-scala-isolated-slick-example/modules}/flyway/src/main/resources/db/migration/V20150409131208__add_user.sql (100%) rename {modules => play-scala-isolated-slick-example/modules}/slick/build.sbt (100%) rename {modules => play-scala-isolated-slick-example/modules}/slick/src/main/resources/application.conf (100%) rename {modules => play-scala-isolated-slick-example/modules}/slick/src/main/scala/com/example/user/slick/SlickUserDAO.scala (100%) rename {project => play-scala-isolated-slick-example/project}/Build.scala (100%) rename {project => play-scala-isolated-slick-example/project}/build.properties (100%) rename {project => play-scala-isolated-slick-example/project}/plugins.sbt (100%) rename {public => play-scala-isolated-slick-example/public}/images/favicon.png (100%) rename {public => play-scala-isolated-slick-example/public}/javascripts/hello.js (100%) rename {public => play-scala-isolated-slick-example/public}/stylesheets/main.css (100%) rename {scripts => play-scala-isolated-slick-example/scripts}/script-helper (100%) rename {scripts => play-scala-isolated-slick-example/scripts}/test-gradle (100%) rename {scripts => play-scala-isolated-slick-example/scripts}/test-sbt (100%) rename {test => play-scala-isolated-slick-example/test}/controller/FunctionalSpec.scala (100%) rename {test => play-scala-isolated-slick-example/test}/controller/MyApplicationFactory.scala (100%) diff --git a/.gitignore b/play-scala-isolated-slick-example/.gitignore similarity index 100% rename from .gitignore rename to play-scala-isolated-slick-example/.gitignore diff --git a/.mergify.yml b/play-scala-isolated-slick-example/.mergify.yml similarity index 100% rename from .mergify.yml rename to play-scala-isolated-slick-example/.mergify.yml diff --git a/.travis.yml b/play-scala-isolated-slick-example/.travis.yml similarity index 100% rename from .travis.yml rename to play-scala-isolated-slick-example/.travis.yml diff --git a/LICENSE b/play-scala-isolated-slick-example/LICENSE similarity index 100% rename from LICENSE rename to play-scala-isolated-slick-example/LICENSE diff --git a/NOTICE b/play-scala-isolated-slick-example/NOTICE similarity index 100% rename from NOTICE rename to play-scala-isolated-slick-example/NOTICE diff --git a/README.md b/play-scala-isolated-slick-example/README.md similarity index 100% rename from README.md rename to play-scala-isolated-slick-example/README.md diff --git a/app/Module.scala b/play-scala-isolated-slick-example/app/Module.scala similarity index 100% rename from app/Module.scala rename to play-scala-isolated-slick-example/app/Module.scala diff --git a/app/controllers/HomeController.scala b/play-scala-isolated-slick-example/app/controllers/HomeController.scala similarity index 100% rename from app/controllers/HomeController.scala rename to play-scala-isolated-slick-example/app/controllers/HomeController.scala diff --git a/app/views/index.scala.html b/play-scala-isolated-slick-example/app/views/index.scala.html similarity index 100% rename from app/views/index.scala.html rename to play-scala-isolated-slick-example/app/views/index.scala.html diff --git a/app/views/main.scala.html b/play-scala-isolated-slick-example/app/views/main.scala.html similarity index 100% rename from app/views/main.scala.html rename to play-scala-isolated-slick-example/app/views/main.scala.html diff --git a/build.sbt b/play-scala-isolated-slick-example/build.sbt similarity index 100% rename from build.sbt rename to play-scala-isolated-slick-example/build.sbt diff --git a/conf/logback.xml b/play-scala-isolated-slick-example/conf/logback.xml similarity index 100% rename from conf/logback.xml rename to play-scala-isolated-slick-example/conf/logback.xml diff --git a/conf/routes b/play-scala-isolated-slick-example/conf/routes similarity index 100% rename from conf/routes rename to play-scala-isolated-slick-example/conf/routes diff --git a/modules/api/src/main/scala/com/example/user/UserDAO.scala b/play-scala-isolated-slick-example/modules/api/src/main/scala/com/example/user/UserDAO.scala similarity index 100% rename from modules/api/src/main/scala/com/example/user/UserDAO.scala rename to play-scala-isolated-slick-example/modules/api/src/main/scala/com/example/user/UserDAO.scala diff --git a/modules/flyway/build.sbt b/play-scala-isolated-slick-example/modules/flyway/build.sbt similarity index 100% rename from modules/flyway/build.sbt rename to play-scala-isolated-slick-example/modules/flyway/build.sbt diff --git a/modules/flyway/src/main/resources/db/migration/V20150409112518__create_users_table.sql b/play-scala-isolated-slick-example/modules/flyway/src/main/resources/db/migration/V20150409112518__create_users_table.sql similarity index 100% rename from modules/flyway/src/main/resources/db/migration/V20150409112518__create_users_table.sql rename to play-scala-isolated-slick-example/modules/flyway/src/main/resources/db/migration/V20150409112518__create_users_table.sql diff --git a/modules/flyway/src/main/resources/db/migration/V20150409131208__add_user.sql b/play-scala-isolated-slick-example/modules/flyway/src/main/resources/db/migration/V20150409131208__add_user.sql similarity index 100% rename from modules/flyway/src/main/resources/db/migration/V20150409131208__add_user.sql rename to play-scala-isolated-slick-example/modules/flyway/src/main/resources/db/migration/V20150409131208__add_user.sql diff --git a/modules/slick/build.sbt b/play-scala-isolated-slick-example/modules/slick/build.sbt similarity index 100% rename from modules/slick/build.sbt rename to play-scala-isolated-slick-example/modules/slick/build.sbt diff --git a/modules/slick/src/main/resources/application.conf b/play-scala-isolated-slick-example/modules/slick/src/main/resources/application.conf similarity index 100% rename from modules/slick/src/main/resources/application.conf rename to play-scala-isolated-slick-example/modules/slick/src/main/resources/application.conf diff --git a/modules/slick/src/main/scala/com/example/user/slick/SlickUserDAO.scala b/play-scala-isolated-slick-example/modules/slick/src/main/scala/com/example/user/slick/SlickUserDAO.scala similarity index 100% rename from modules/slick/src/main/scala/com/example/user/slick/SlickUserDAO.scala rename to play-scala-isolated-slick-example/modules/slick/src/main/scala/com/example/user/slick/SlickUserDAO.scala diff --git a/project/Build.scala b/play-scala-isolated-slick-example/project/Build.scala similarity index 100% rename from project/Build.scala rename to play-scala-isolated-slick-example/project/Build.scala diff --git a/project/build.properties b/play-scala-isolated-slick-example/project/build.properties similarity index 100% rename from project/build.properties rename to play-scala-isolated-slick-example/project/build.properties diff --git a/project/plugins.sbt b/play-scala-isolated-slick-example/project/plugins.sbt similarity index 100% rename from project/plugins.sbt rename to play-scala-isolated-slick-example/project/plugins.sbt diff --git a/public/images/favicon.png b/play-scala-isolated-slick-example/public/images/favicon.png similarity index 100% rename from public/images/favicon.png rename to play-scala-isolated-slick-example/public/images/favicon.png diff --git a/public/javascripts/hello.js b/play-scala-isolated-slick-example/public/javascripts/hello.js similarity index 100% rename from public/javascripts/hello.js rename to play-scala-isolated-slick-example/public/javascripts/hello.js diff --git a/public/stylesheets/main.css b/play-scala-isolated-slick-example/public/stylesheets/main.css similarity index 100% rename from public/stylesheets/main.css rename to play-scala-isolated-slick-example/public/stylesheets/main.css diff --git a/scripts/script-helper b/play-scala-isolated-slick-example/scripts/script-helper similarity index 100% rename from scripts/script-helper rename to play-scala-isolated-slick-example/scripts/script-helper diff --git a/scripts/test-gradle b/play-scala-isolated-slick-example/scripts/test-gradle similarity index 100% rename from scripts/test-gradle rename to play-scala-isolated-slick-example/scripts/test-gradle diff --git a/scripts/test-sbt b/play-scala-isolated-slick-example/scripts/test-sbt similarity index 100% rename from scripts/test-sbt rename to play-scala-isolated-slick-example/scripts/test-sbt diff --git a/test/controller/FunctionalSpec.scala b/play-scala-isolated-slick-example/test/controller/FunctionalSpec.scala similarity index 100% rename from test/controller/FunctionalSpec.scala rename to play-scala-isolated-slick-example/test/controller/FunctionalSpec.scala diff --git a/test/controller/MyApplicationFactory.scala b/play-scala-isolated-slick-example/test/controller/MyApplicationFactory.scala similarity index 100% rename from test/controller/MyApplicationFactory.scala rename to play-scala-isolated-slick-example/test/controller/MyApplicationFactory.scala