From cb02c7eaa654bdc3488ed556ffe522711ab8478b Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Tue, 19 Apr 2016 19:18:58 -0700 Subject: [PATCH 01/66] Initial commit --- .gitignore | 64 +++++++++++++++++++++ LICENSE | 8 +++ README.md | 82 +++++++++++++++++++++++++++ app/MyApplicationLoader.scala | 28 +++++++++ app/controllers/HomeController.scala | 24 ++++++++ app/views/index.scala.html | 20 +++++++ app/views/main.scala.html | 23 ++++++++ build.sbt | 14 +++++ conf/application.conf | 2 + conf/logback.xml | 43 ++++++++++++++ conf/routes | 9 +++ project/build.properties | 1 + project/plugins.sbt | 2 + public/images/favicon.png | Bin 0 -> 687 bytes public/javascripts/hello.js | 3 + public/stylesheets/main.css | 0 test/ApplicationSpec.scala | 43 ++++++++++++++ test/CompileTimeComponents.scala | 15 +++++ test/SeleniumSpec.scala | 14 +++++ test/ServerSpec.scala | 29 ++++++++++ 20 files changed, 424 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 app/MyApplicationLoader.scala create mode 100644 app/controllers/HomeController.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/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/CompileTimeComponents.scala create mode 100644 test/SeleniumSpec.scala create mode 100644 test/ServerSpec.scala diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..1db792587 --- /dev/null +++ b/.gitignore @@ -0,0 +1,64 @@ +logs +target +/.idea +/.idea_modules +/.classpath +/.project +/.settings +/RUNNING_PID + +/db +.eclipse +/lib/ +/logs/ +/modules +/project/target +/target +tmp/ +test-result +server.pid +*.eml +/dist/ +.cache + +# User-specific stuff: +.idea/workspace.xml +.idea/tasks.xml +.idea/dictionaries +.idea/vcs.xml +.idea/jsLibraryMappings.xml + +# Sensitive or high-churn files: +.idea/dataSources.ids +.idea/dataSources.xml +.idea/dataSources.local.xml +.idea/sqlDataSources.xml +.idea/dynamic.xml +.idea/uiDesigner.xml + +# Gradle: +.idea/gradle.xml +.idea/libraries + +# Mongo Explorer plugin: +.idea/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties 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.md b/README.md new file mode 100644 index 000000000..d07a8df2b --- /dev/null +++ b/README.md @@ -0,0 +1,82 @@ +# Play Framework with Compile Time DI Tests + +This is an example of Play 2.5.x using the Scala API with manually wired compile time dependency injection. + +The application loader here is `MyApplicationLoader` which uses `MyComponents` to wire together an injector. In testing, there are some places where a `WSClient` has to be used, and so some additional components have to be added in. + +To do this, the injector has to be implemented specifying all of the built in components, plus the WS API, which is made available through `AhcWSComponents`: + + +``` +import play.api._ +import play.api.i18n._ +import play.api.inject._ +import play.api.libs.ws.ahc.AhcWSComponents +import play.api.routing.Router +import router.Routes + +class MyApplicationLoader extends ApplicationLoader { + def load(context: ApplicationLoader.Context) = { + LoggerConfigurator(context.environment.classLoader).foreach { + _.configure(context.environment) + } + new MyComponents(context).application + } +} + +class MyComponents(context: ApplicationLoader.Context) + extends BuiltInComponentsFromContext(context) + with I18nComponents + with AhcWSComponents { + + override lazy val injector = new SimpleInjector(NewInstanceInjector) + router + cookieSigner + csrfTokenSigner + httpConfiguration + tempFileCreator + global + crypto + wsApi + messagesApi + + lazy val router: Router = new Routes(httpErrorHandler, homeController, assets) + + lazy val homeController = new controllers.HomeController() + lazy val assets = new controllers.Assets(httpErrorHandler) +} +``` + +Now that `MyComponents` has the `AhcWSComponents` trait, it can use `components.wsClient` anywhere. It's most convenient to define the components in a trait for testing: + +``` +trait CompileTimeComponents { + + lazy val components = { + val classLoader = ApplicationLoader.getClass.getClassLoader + val env = new Environment(new java.io.File("."), classLoader, Mode.Test) + val context = ApplicationLoader.createContext(env) + new MyComponents(context) + } + +} +``` + +Once the `CompileTimeComponents` is defined, then a specification can use `components.application` to expose the application to ScalaTest and call out to the client: + +``` +class ServerSpec extends PlaySpec + with Results + with CompileTimeComponents + with OneServerPerSuite + with ScalaFutures { + + override implicit lazy val app: Application = components.application + + "Server query should" should { + + "work" in { + implicit val ec = app.materializer.executionContext + val wsClient = components.wsClient + + whenReady(wsUrl("/")(portNumber, wsClient).get) { response => + response.status mustBe OK + } + } + } + +} +``` + +Please refer to https://www.playframework.com/documentation/2.5.x/ScalaCompileTimeDependencyInjection and https://www.playframework.com/documentation/2.5.x/ScalaTestingWithScalaTest#Using-ScalaTest-+-Play for documentation. diff --git a/app/MyApplicationLoader.scala b/app/MyApplicationLoader.scala new file mode 100644 index 000000000..cd27a4965 --- /dev/null +++ b/app/MyApplicationLoader.scala @@ -0,0 +1,28 @@ +import play.api._ +import play.api.i18n._ +import play.api.inject._ +import play.api.libs.ws.ahc.AhcWSComponents +import play.api.routing.Router +import router.Routes + +class MyApplicationLoader extends ApplicationLoader { + def load(context: ApplicationLoader.Context) = { + LoggerConfigurator(context.environment.classLoader).foreach { + _.configure(context.environment) + } + new MyComponents(context).application + } +} + +class MyComponents(context: ApplicationLoader.Context) + extends BuiltInComponentsFromContext(context) + with I18nComponents + with AhcWSComponents { + + override lazy val injector = new SimpleInjector(NewInstanceInjector) + router + cookieSigner + csrfTokenSigner + httpConfiguration + tempFileCreator + global + crypto + wsApi + messagesApi + + lazy val router: Router = new Routes(httpErrorHandler, homeController, assets) + + lazy val homeController = new controllers.HomeController() + lazy val assets = new controllers.Assets(httpErrorHandler) +} diff --git a/app/controllers/HomeController.scala b/app/controllers/HomeController.scala new file mode 100644 index 000000000..b2d8a9078 --- /dev/null +++ b/app/controllers/HomeController.scala @@ -0,0 +1,24 @@ +package controllers + +import javax.inject._ + +import play.api.mvc._ + +/** + * This controller creates an `Action` to handle HTTP requests to the + * application's home page. + */ +@Singleton +class HomeController @Inject() extends Controller { + + /** + * Create an Action to render an HTML page with a welcome message. + * The configuration in the `routes` file means that this method + * will be called when the application receives a `GET` request with + * a path of `/`. + */ + def index = Action { + Ok(views.html.index("Your new application is ready.")) + } + +} diff --git a/app/views/index.scala.html b/app/views/index.scala.html new file mode 100644 index 000000000..da59cbfe0 --- /dev/null +++ b/app/views/index.scala.html @@ -0,0 +1,20 @@ +@* + * This template takes a single argument, a String containing a + * message to display. + *@ +@(message: String) + +@* + * Call the `main` template with two arguments. The first + * argument is a `String` with the title of the page, the second + * argument is an `Html` object containing the body of the page. + *@ +@main("Welcome to Play") { + + @* + * Get an `Html` object by calling the built-in Play welcome + * template and passing a `String` message. + *@ + @play20.welcome(message, style = "Scala") + +} diff --git a/app/views/main.scala.html b/app/views/main.scala.html new file mode 100644 index 000000000..9414f4be6 --- /dev/null +++ b/app/views/main.scala.html @@ -0,0 +1,23 @@ +@* + * This template is called from the `index` template. This template + * handles the rendering of the page header and body tags. It takes + * two arguments, a `String` for the title of the page and an `Html` + * object to insert into the body of the page. + *@ +@(title: String)(content: Html) + + + + + @* Here's where we render the page title `String`. *@ + @title + + + + + + @* And here's where we render the `Html` object containing + * the page content. *@ + @content + + diff --git a/build.sbt b/build.sbt new file mode 100644 index 000000000..9399c8189 --- /dev/null +++ b/build.sbt @@ -0,0 +1,14 @@ +name := """play-2.5.x-scala-compile-di-with-tests""" + +version := "1.0-SNAPSHOT" + +lazy val root = (project in file(".")).enablePlugins(PlayScala) + +scalaVersion := "2.11.7" + +libraryDependencies ++= Seq( + ws, + "org.scalatestplus.play" %% "scalatestplus-play" % "1.5.1" % Test +) + +resolvers += "scalaz-bintray" at "http://dl.bintray.com/scalaz/releases" diff --git a/conf/application.conf b/conf/application.conf new file mode 100644 index 000000000..397194ccf --- /dev/null +++ b/conf/application.conf @@ -0,0 +1,2 @@ + +play.application.loader=MyApplicationLoader \ No newline at end of file diff --git a/conf/logback.xml b/conf/logback.xml new file mode 100644 index 000000000..d21cd69d3 --- /dev/null +++ b/conf/logback.xml @@ -0,0 +1,43 @@ + + + + + + + ${application.home:-.}/logs/application.log + + %date [%level] from %logger in %thread - %message%n%xException + + + + + + %coloredLevel %logger{15} - %message%n%xException{10} + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/conf/routes b/conf/routes new file mode 100644 index 000000000..18027a30a --- /dev/null +++ b/conf/routes @@ -0,0 +1,9 @@ +# Routes +# This file defines all application routes (Higher priority routes first) +# ~~~~ + +# An example controller showing a sample home page +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/project/build.properties b/project/build.properties new file mode 100644 index 000000000..43b8278c6 --- /dev/null +++ b/project/build.properties @@ -0,0 +1 @@ +sbt.version=0.13.11 diff --git a/project/plugins.sbt b/project/plugins.sbt new file mode 100644 index 000000000..1f45b0514 --- /dev/null +++ b/project/plugins.sbt @@ -0,0 +1,2 @@ +// The Play plugin +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.5.2") 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..02ee13c7c --- /dev/null +++ b/public/javascripts/hello.js @@ -0,0 +1,3 @@ +if (window.console) { + console.log("Welcome to your Play application's JavaScript!"); +} 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..1096bd462 --- /dev/null +++ b/test/ApplicationSpec.scala @@ -0,0 +1,43 @@ +import org.scalatest._ +import org.scalatestplus.play._ +import play.api.test._ +import play.api.test.Helpers._ +import play.api._ + +/** + * Add your spec here. + * You can mock out a whole application including requests, plugins etc. + * For more information, consult the wiki. + */ +class ApplicationSpec extends PlaySpec with OneAppPerTest with CompileTimeComponents { + + override def newAppForTest(testData: TestData): Application = { + val loader = new MyApplicationLoader() + val classLoader = ApplicationLoader.getClass.getClassLoader + val env = new Environment(new java.io.File("."), classLoader, Mode.Test) + val context = ApplicationLoader.createContext(env) + components.application + } + + "Routes" should { + + "send 404 on a bad request" in { + route(app, FakeRequest(GET, "/boum")).map(status(_)) mustBe Some(NOT_FOUND) + } + + } + + "HomeController" should { + + "render the index page" in { + val home = route(app, FakeRequest(GET, "/")).get + + status(home) mustBe OK + contentType(home) mustBe Some("text/html") + contentAsString(home) must include ("Your new application is ready.") + } + + } + + +} diff --git a/test/CompileTimeComponents.scala b/test/CompileTimeComponents.scala new file mode 100644 index 000000000..88fed6778 --- /dev/null +++ b/test/CompileTimeComponents.scala @@ -0,0 +1,15 @@ +import play.api.{ApplicationLoader, Environment, Mode} + +/** + * Exposes the components in the application loader to the various specs. + */ +trait CompileTimeComponents { + + lazy val components = { + val classLoader = ApplicationLoader.getClass.getClassLoader + val env = new Environment(new java.io.File("."), classLoader, Mode.Test) + val context = ApplicationLoader.createContext(env) + new MyComponents(context) + } + +} diff --git a/test/SeleniumSpec.scala b/test/SeleniumSpec.scala new file mode 100644 index 000000000..953a17d10 --- /dev/null +++ b/test/SeleniumSpec.scala @@ -0,0 +1,14 @@ +import org.scalatestplus.play._ + +class SeleniumSpec extends PlaySpec with OneServerPerTest with OneBrowserPerTest with HtmlUnitFactory { + + "SeleniumSpec" should { + + "work from within a browser" in { + + go to ("http://localhost:" + port) + + pageSource must include ("Your new application is ready.") + } + } +} diff --git a/test/ServerSpec.scala b/test/ServerSpec.scala new file mode 100644 index 000000000..54fe6d2ec --- /dev/null +++ b/test/ServerSpec.scala @@ -0,0 +1,29 @@ +import org.scalatest.concurrent.ScalaFutures +import org.scalatestplus.play._ +import play.api.Application +import play.api.mvc.Results +import play.api.test.Helpers._ + +class ServerSpec extends PlaySpec + with Results + with CompileTimeComponents + with OneServerPerSuite + with ScalaFutures { + + override implicit lazy val app: Application = components.application + + "Server query should" should { + + "work" in { + implicit val ec = app.materializer.executionContext + val wsClient = components.wsClient + + whenReady(wsUrl("/")(portNumber, wsClient).get) { response => + response.status mustBe OK + } + } + + } + +} + From c2f165948c443011f57f5f1c858f85bc2ed3e7ca Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Tue, 19 Apr 2016 21:25:03 -0700 Subject: [PATCH 02/66] Update with component suites --- README.md | 39 +++++++++++++++++------------- test/ApplicationSpec.scala | 17 +++---------- test/CompileTimeComponents.scala | 33 ++++++++++++++++--------- test/ScalaTestWithComponents.scala | 32 ++++++++++++++++++++++++ test/ServerSpec.scala | 6 +---- test/WithComponents.scala | 16 ++++++++++++ 6 files changed, 97 insertions(+), 46 deletions(-) create mode 100644 test/ScalaTestWithComponents.scala create mode 100644 test/WithComponents.scala diff --git a/README.md b/README.md index d07a8df2b..1d0b39a15 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,7 @@ The application loader here is `MyApplicationLoader` which uses `MyComponents` t To do this, the injector has to be implemented specifying all of the built in components, plus the WS API, which is made available through `AhcWSComponents`: - -``` +``` scala import play.api._ import play.api.i18n._ import play.api.inject._ @@ -38,32 +37,38 @@ class MyComponents(context: ApplicationLoader.Context) } ``` -Now that `MyComponents` has the `AhcWSComponents` trait, it can use `components.wsClient` anywhere. It's most convenient to define the components in a trait for testing: +Now that `MyComponents` has the `AhcWSComponents` trait, it can use `components.wsClient` anywhere. -``` -trait CompileTimeComponents { +The ScalaTest suite mixins such as `OneAppPerSuite` use `GuiceApplicationLoader` for all the implicit Application set up, so to use dependency injection, the trait must be extended to use the components using types: - lazy val components = { - val classLoader = ApplicationLoader.getClass.getClassLoader - val env = new Environment(new java.io.File("."), classLoader, Mode.Test) - val context = ApplicationLoader.createContext(env) - new MyComponents(context) - } +``` scala +trait OneServerPerSuiteWithComponents[T <: BuiltInComponents] extends OneServerPerSuite with WithContext with WithComponents[T] { + this: Suite => + lazy val components: T = createComponents(context) + + override implicit lazy val app: Application = components.application } ``` -Once the `CompileTimeComponents` is defined, then a specification can use `components.application` to expose the application to ScalaTest and call out to the client: +Then, depending on your components, you can set up a specific instance + +``` scala +trait OneServerPerSuiteWithMyComponents extends OneServerPerSuiteWithComponents[MyComponents] { + this: Suite => + override def createComponents(context: Context): MyComponents = new MyComponents(context) +} ``` + +Once the `OneServerPerSuiteWithMyComponents` is defined, you can access the `components` field to get at the wsClient: + +``` scala class ServerSpec extends PlaySpec + with OneServerPerSuiteWithMyComponents with Results - with CompileTimeComponents - with OneServerPerSuite with ScalaFutures { - override implicit lazy val app: Application = components.application - "Server query should" should { "work" in { @@ -74,8 +79,8 @@ class ServerSpec extends PlaySpec response.status mustBe OK } } - } + } } ``` diff --git a/test/ApplicationSpec.scala b/test/ApplicationSpec.scala index 1096bd462..0c9b40b0d 100644 --- a/test/ApplicationSpec.scala +++ b/test/ApplicationSpec.scala @@ -1,5 +1,6 @@ import org.scalatest._ import org.scalatestplus.play._ +import play.api.ApplicationLoader.Context import play.api.test._ import play.api.test.Helpers._ import play.api._ @@ -9,19 +10,11 @@ import play.api._ * You can mock out a whole application including requests, plugins etc. * For more information, consult the wiki. */ -class ApplicationSpec extends PlaySpec with OneAppPerTest with CompileTimeComponents { - - override def newAppForTest(testData: TestData): Application = { - val loader = new MyApplicationLoader() - val classLoader = ApplicationLoader.getClass.getClassLoader - val env = new Environment(new java.io.File("."), classLoader, Mode.Test) - val context = ApplicationLoader.createContext(env) - components.application - } +class ApplicationSpec extends PlaySpec with OneAppPerTestWithMyComponents { "Routes" should { - "send 404 on a bad request" in { + "send 404 on a bad request" in { route(app, FakeRequest(GET, "/boum")).map(status(_)) mustBe Some(NOT_FOUND) } @@ -34,10 +27,8 @@ class ApplicationSpec extends PlaySpec with OneAppPerTest with CompileTimeCompon status(home) mustBe OK contentType(home) mustBe Some("text/html") - contentAsString(home) must include ("Your new application is ready.") + contentAsString(home) must include("Your new application is ready.") } } - - } diff --git a/test/CompileTimeComponents.scala b/test/CompileTimeComponents.scala index 88fed6778..b6a8381b6 100644 --- a/test/CompileTimeComponents.scala +++ b/test/CompileTimeComponents.scala @@ -1,15 +1,26 @@ -import play.api.{ApplicationLoader, Environment, Mode} +import org.scalatest.Suite +import play.api.ApplicationLoader.Context -/** - * Exposes the components in the application loader to the various specs. - */ -trait CompileTimeComponents { +trait OneAppPerTestWithMyComponents extends OneAppPerTestWithComponents[MyComponents] { + this: Suite => - lazy val components = { - val classLoader = ApplicationLoader.getClass.getClassLoader - val env = new Environment(new java.io.File("."), classLoader, Mode.Test) - val context = ApplicationLoader.createContext(env) - new MyComponents(context) - } + override def createComponents(context: Context): MyComponents = new MyComponents(context) +} + +trait OneAppPerSuiteWithMyComponents extends OneAppPerTestWithComponents[MyComponents] { + this: Suite => + + override def createComponents(context: Context): MyComponents = new MyComponents(context) +} + +trait OneServerPerTestWithMyComponents extends OneServerPerSuiteWithComponents[MyComponents] { + this: Suite => + + override def createComponents(context: Context): MyComponents = new MyComponents(context) +} + +trait OneServerPerSuiteWithMyComponents extends OneServerPerSuiteWithComponents[MyComponents] { + this: Suite => + override def createComponents(context: Context): MyComponents = new MyComponents(context) } diff --git a/test/ScalaTestWithComponents.scala b/test/ScalaTestWithComponents.scala new file mode 100644 index 000000000..f93546bf3 --- /dev/null +++ b/test/ScalaTestWithComponents.scala @@ -0,0 +1,32 @@ +import org.scalatest.{Suite, TestData} +import org.scalatestplus.play.{OneAppPerSuite, OneAppPerTest, OneServerPerSuite, OneServerPerTest} +import play.api.{BuiltInComponents, _} + +trait OneAppPerTestWithComponents[T <: BuiltInComponents] extends OneAppPerTest with WithContext with WithComponents[T] { + this: Suite => + + override def newAppForTest(testData: TestData): Application = createComponents(context).application +} + +trait OneAppPerSuiteWithComponents[T <: BuiltInComponents] extends OneAppPerSuite with WithContext with WithComponents[T] { + this: Suite => + + lazy val components: T = createComponents(context) + + override implicit lazy val app: Application = components.application +} + +trait OneServerPerTestWithComponents[T <: BuiltInComponents] extends OneServerPerTest with WithContext with WithComponents[T] { + this: Suite => + + override def newAppForTest(testData: TestData): Application = createComponents(context).application +} + +trait OneServerPerSuiteWithComponents[T <: BuiltInComponents] extends OneServerPerSuite with WithContext with WithComponents[T] { + this: Suite => + + lazy val components: T = createComponents(context) + + override implicit lazy val app: Application = components.application +} + diff --git a/test/ServerSpec.scala b/test/ServerSpec.scala index 54fe6d2ec..11ef7b770 100644 --- a/test/ServerSpec.scala +++ b/test/ServerSpec.scala @@ -1,17 +1,13 @@ import org.scalatest.concurrent.ScalaFutures import org.scalatestplus.play._ -import play.api.Application import play.api.mvc.Results import play.api.test.Helpers._ class ServerSpec extends PlaySpec + with OneServerPerSuiteWithMyComponents with Results - with CompileTimeComponents - with OneServerPerSuite with ScalaFutures { - override implicit lazy val app: Application = components.application - "Server query should" should { "work" in { diff --git a/test/WithComponents.scala b/test/WithComponents.scala new file mode 100644 index 000000000..9a5c466d2 --- /dev/null +++ b/test/WithComponents.scala @@ -0,0 +1,16 @@ +import play.api.ApplicationLoader.Context +import play.api.{ApplicationLoader, BuiltInComponents, Environment, Mode} + +trait WithComponents[T <: BuiltInComponents] { + def createComponents(context: Context): T +} + +trait WithContext { + + def context: ApplicationLoader.Context = { + val classLoader = ApplicationLoader.getClass.getClassLoader + val env = new Environment(new java.io.File("."), classLoader, Mode.Test) + ApplicationLoader.createContext(env) + } + +} From 226090cbc387cfe2a4dcdf5fa3a83b67b7d82493 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Tue, 19 Apr 2016 22:12:11 -0700 Subject: [PATCH 03/66] Better formatting --- README.md | 21 ++++++++++++++++++--- app/MyApplicationLoader.scala | 13 ++++++++++++- test/CompileTimeComponents.scala | 12 ++++++++---- test/ScalaTestWithComponents.scala | 20 ++++++++++++++++---- test/SeleniumSpec.scala | 6 +++++- 5 files changed, 59 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 1d0b39a15..f00dd30e5 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,18 @@ class MyComponents(context: ApplicationLoader.Context) with I18nComponents with AhcWSComponents { - override lazy val injector = new SimpleInjector(NewInstanceInjector) + router + cookieSigner + csrfTokenSigner + httpConfiguration + tempFileCreator + global + crypto + wsApi + messagesApi + override lazy val injector = { + new SimpleInjector(NewInstanceInjector) + + router + + cookieSigner + + csrfTokenSigner + + httpConfiguration + + tempFileCreator + + global + + crypto + + wsApi + + messagesApi + } lazy val router: Router = new Routes(httpErrorHandler, homeController, assets) @@ -42,7 +53,10 @@ Now that `MyComponents` has the `AhcWSComponents` trait, it can use `components. The ScalaTest suite mixins such as `OneAppPerSuite` use `GuiceApplicationLoader` for all the implicit Application set up, so to use dependency injection, the trait must be extended to use the components using types: ``` scala -trait OneServerPerSuiteWithComponents[T <: BuiltInComponents] extends OneServerPerSuite with WithContext with WithComponents[T] { +trait OneServerPerSuiteWithComponents[T <: BuiltInComponents] + extends OneServerPerSuite + with WithContext + with WithComponents[T] { this: Suite => lazy val components: T = createComponents(context) @@ -54,7 +68,8 @@ trait OneServerPerSuiteWithComponents[T <: BuiltInComponents] extends OneServerP Then, depending on your components, you can set up a specific instance ``` scala -trait OneServerPerSuiteWithMyComponents extends OneServerPerSuiteWithComponents[MyComponents] { +trait OneServerPerSuiteWithMyComponents + extends OneServerPerSuiteWithComponents[MyComponents] { this: Suite => override def createComponents(context: Context): MyComponents = new MyComponents(context) diff --git a/app/MyApplicationLoader.scala b/app/MyApplicationLoader.scala index cd27a4965..317d5c120 100644 --- a/app/MyApplicationLoader.scala +++ b/app/MyApplicationLoader.scala @@ -19,7 +19,18 @@ class MyComponents(context: ApplicationLoader.Context) with I18nComponents with AhcWSComponents { - override lazy val injector = new SimpleInjector(NewInstanceInjector) + router + cookieSigner + csrfTokenSigner + httpConfiguration + tempFileCreator + global + crypto + wsApi + messagesApi + override lazy val injector = { + new SimpleInjector(NewInstanceInjector) + + router + + cookieSigner + + csrfTokenSigner + + httpConfiguration + + tempFileCreator + + global + + crypto + + wsApi + + messagesApi + } lazy val router: Router = new Routes(httpErrorHandler, homeController, assets) diff --git a/test/CompileTimeComponents.scala b/test/CompileTimeComponents.scala index b6a8381b6..12c87d924 100644 --- a/test/CompileTimeComponents.scala +++ b/test/CompileTimeComponents.scala @@ -1,25 +1,29 @@ import org.scalatest.Suite import play.api.ApplicationLoader.Context -trait OneAppPerTestWithMyComponents extends OneAppPerTestWithComponents[MyComponents] { +trait OneAppPerTestWithMyComponents + extends OneAppPerTestWithComponents[MyComponents] { this: Suite => override def createComponents(context: Context): MyComponents = new MyComponents(context) } -trait OneAppPerSuiteWithMyComponents extends OneAppPerTestWithComponents[MyComponents] { +trait OneAppPerSuiteWithMyComponents + extends OneAppPerTestWithComponents[MyComponents] { this: Suite => override def createComponents(context: Context): MyComponents = new MyComponents(context) } -trait OneServerPerTestWithMyComponents extends OneServerPerSuiteWithComponents[MyComponents] { +trait OneServerPerTestWithMyComponents + extends OneServerPerSuiteWithComponents[MyComponents] { this: Suite => override def createComponents(context: Context): MyComponents = new MyComponents(context) } -trait OneServerPerSuiteWithMyComponents extends OneServerPerSuiteWithComponents[MyComponents] { +trait OneServerPerSuiteWithMyComponents + extends OneServerPerSuiteWithComponents[MyComponents] { this: Suite => override def createComponents(context: Context): MyComponents = new MyComponents(context) diff --git a/test/ScalaTestWithComponents.scala b/test/ScalaTestWithComponents.scala index f93546bf3..76825bb34 100644 --- a/test/ScalaTestWithComponents.scala +++ b/test/ScalaTestWithComponents.scala @@ -2,13 +2,19 @@ import org.scalatest.{Suite, TestData} import org.scalatestplus.play.{OneAppPerSuite, OneAppPerTest, OneServerPerSuite, OneServerPerTest} import play.api.{BuiltInComponents, _} -trait OneAppPerTestWithComponents[T <: BuiltInComponents] extends OneAppPerTest with WithContext with WithComponents[T] { +trait OneAppPerTestWithComponents[T <: BuiltInComponents] + extends OneAppPerTest + with WithContext + with WithComponents[T] { this: Suite => override def newAppForTest(testData: TestData): Application = createComponents(context).application } -trait OneAppPerSuiteWithComponents[T <: BuiltInComponents] extends OneAppPerSuite with WithContext with WithComponents[T] { +trait OneAppPerSuiteWithComponents[T <: BuiltInComponents] + extends OneAppPerSuite + with WithContext + with WithComponents[T] { this: Suite => lazy val components: T = createComponents(context) @@ -16,13 +22,19 @@ trait OneAppPerSuiteWithComponents[T <: BuiltInComponents] extends OneAppPerSuit override implicit lazy val app: Application = components.application } -trait OneServerPerTestWithComponents[T <: BuiltInComponents] extends OneServerPerTest with WithContext with WithComponents[T] { +trait OneServerPerTestWithComponents[T <: BuiltInComponents] + extends OneServerPerTest + with WithContext + with WithComponents[T] { this: Suite => override def newAppForTest(testData: TestData): Application = createComponents(context).application } -trait OneServerPerSuiteWithComponents[T <: BuiltInComponents] extends OneServerPerSuite with WithContext with WithComponents[T] { +trait OneServerPerSuiteWithComponents[T <: BuiltInComponents] + extends OneServerPerSuite + with WithContext + with WithComponents[T] { this: Suite => lazy val components: T = createComponents(context) diff --git a/test/SeleniumSpec.scala b/test/SeleniumSpec.scala index 953a17d10..6d7c245db 100644 --- a/test/SeleniumSpec.scala +++ b/test/SeleniumSpec.scala @@ -1,6 +1,10 @@ import org.scalatestplus.play._ -class SeleniumSpec extends PlaySpec with OneServerPerTest with OneBrowserPerTest with HtmlUnitFactory { +class SeleniumSpec + extends PlaySpec + with OneServerPerTest + with OneBrowserPerTest + with HtmlUnitFactory { "SeleniumSpec" should { From ab0ad6dd629affb5408a69c4be3f02e20df1c980 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Wed, 20 Apr 2016 09:12:49 -0700 Subject: [PATCH 04/66] Merge WithContext and WithComponents --- README.md | 35 +++++++++++++++++++++++----- test/ScalaTestWithComponents.scala | 25 +++++++------------- test/WithApplicationComponents.scala | 24 +++++++++++++++++++ test/WithComponents.scala | 16 ------------- 4 files changed, 61 insertions(+), 39 deletions(-) create mode 100644 test/WithApplicationComponents.scala delete mode 100644 test/WithComponents.scala diff --git a/README.md b/README.md index f00dd30e5..e145890fa 100644 --- a/README.md +++ b/README.md @@ -55,17 +55,40 @@ The ScalaTest suite mixins such as `OneAppPerSuite` use `GuiceApplicationLoader` ``` scala trait OneServerPerSuiteWithComponents[T <: BuiltInComponents] extends OneServerPerSuite - with WithContext - with WithComponents[T] { + with WithApplicationComponents[T] { this: Suite => - lazy val components: T = createComponents(context) + override implicit lazy val app: Application = newApplication +} +``` + +where `WithApplicationComponents` is defined as: + +``` scala +trait WithApplicationComponents[T <: BuiltInComponents] { + private var _components: T = _ - override implicit lazy val app: Application = components.application + // accessed to get the components in tests + final def components: T = _components + + // overridden by subclasses + def createComponents(context: Context): T + + // creates a new application and sets the components + def newApplication: Application = { + _components = createComponents(context) + _components.application + } + + def context: ApplicationLoader.Context = { + val classLoader = ApplicationLoader.getClass.getClassLoader + val env = new Environment(new java.io.File("."), classLoader, Mode.Test) + ApplicationLoader.createContext(env) + } } ``` -Then, depending on your components, you can set up a specific instance +Then, depending on your components, you can set up a subtype of `OneServerPerSuiteWithComponents` using `MyComponents`: ``` scala trait OneServerPerSuiteWithMyComponents @@ -76,7 +99,7 @@ trait OneServerPerSuiteWithMyComponents } ``` -Once the `OneServerPerSuiteWithMyComponents` is defined, you can access the `components` field to get at the wsClient: +Once the `OneServerPerSuiteWithMyComponents` is defined, you must call out to the `app` lazy val, after which you can access the `components` field to get at the wsClient: ``` scala class ServerSpec extends PlaySpec diff --git a/test/ScalaTestWithComponents.scala b/test/ScalaTestWithComponents.scala index 76825bb34..1231bcc3a 100644 --- a/test/ScalaTestWithComponents.scala +++ b/test/ScalaTestWithComponents.scala @@ -4,41 +4,32 @@ import play.api.{BuiltInComponents, _} trait OneAppPerTestWithComponents[T <: BuiltInComponents] extends OneAppPerTest - with WithContext - with WithComponents[T] { + with WithApplicationComponents[T] { this: Suite => - override def newAppForTest(testData: TestData): Application = createComponents(context).application + override def newAppForTest(testData: TestData): Application = newApplication } trait OneAppPerSuiteWithComponents[T <: BuiltInComponents] extends OneAppPerSuite - with WithContext - with WithComponents[T] { + with WithApplicationComponents[T] { this: Suite => - - lazy val components: T = createComponents(context) - - override implicit lazy val app: Application = components.application + override implicit lazy val app: Application = newApplication } trait OneServerPerTestWithComponents[T <: BuiltInComponents] extends OneServerPerTest - with WithContext - with WithComponents[T] { + with WithApplicationComponents[T] { this: Suite => - override def newAppForTest(testData: TestData): Application = createComponents(context).application + override def newAppForTest(testData: TestData): Application = newApplication } trait OneServerPerSuiteWithComponents[T <: BuiltInComponents] extends OneServerPerSuite - with WithContext - with WithComponents[T] { + with WithApplicationComponents[T] { this: Suite => - lazy val components: T = createComponents(context) - - override implicit lazy val app: Application = components.application + override implicit lazy val app: Application = newApplication } diff --git a/test/WithApplicationComponents.scala b/test/WithApplicationComponents.scala new file mode 100644 index 000000000..dd0115bf2 --- /dev/null +++ b/test/WithApplicationComponents.scala @@ -0,0 +1,24 @@ +import play.api.ApplicationLoader.Context +import play.api._ + +trait WithApplicationComponents[T <: BuiltInComponents] { + private var _components: T = _ + + // accessed to get the components in tests + final def components: T = _components + + // overridden by subclasses + def createComponents(context: Context): T + + // creates a new application and sets the components + def newApplication: Application = { + _components = createComponents(context) + _components.application + } + + def context: ApplicationLoader.Context = { + val classLoader = ApplicationLoader.getClass.getClassLoader + val env = new Environment(new java.io.File("."), classLoader, Mode.Test) + ApplicationLoader.createContext(env) + } +} diff --git a/test/WithComponents.scala b/test/WithComponents.scala deleted file mode 100644 index 9a5c466d2..000000000 --- a/test/WithComponents.scala +++ /dev/null @@ -1,16 +0,0 @@ -import play.api.ApplicationLoader.Context -import play.api.{ApplicationLoader, BuiltInComponents, Environment, Mode} - -trait WithComponents[T <: BuiltInComponents] { - def createComponents(context: Context): T -} - -trait WithContext { - - def context: ApplicationLoader.Context = { - val classLoader = ApplicationLoader.getClass.getClassLoader - val env = new Environment(new java.io.File("."), classLoader, Mode.Test) - ApplicationLoader.createContext(env) - } - -} From f8583abf7d841e4652d894676b61064dbed17bf6 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Tue, 26 Apr 2016 13:41:43 -0700 Subject: [PATCH 05/66] update tests, remove annotations --- app/controllers/HomeController.scala | 6 ++---- test/CompileTimeComponents.scala | 4 ++-- test/SeleniumSpec.scala | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/app/controllers/HomeController.scala b/app/controllers/HomeController.scala index b2d8a9078..34debb1a6 100644 --- a/app/controllers/HomeController.scala +++ b/app/controllers/HomeController.scala @@ -1,15 +1,13 @@ package controllers -import javax.inject._ - import play.api.mvc._ /** * This controller creates an `Action` to handle HTTP requests to the * application's home page. */ -@Singleton -class HomeController @Inject() extends Controller { + +class HomeController extends Controller { /** * Create an Action to render an HTML page with a welcome message. diff --git a/test/CompileTimeComponents.scala b/test/CompileTimeComponents.scala index 12c87d924..14c173df4 100644 --- a/test/CompileTimeComponents.scala +++ b/test/CompileTimeComponents.scala @@ -9,14 +9,14 @@ trait OneAppPerTestWithMyComponents } trait OneAppPerSuiteWithMyComponents - extends OneAppPerTestWithComponents[MyComponents] { + extends OneAppPerSuiteWithComponents[MyComponents] { this: Suite => override def createComponents(context: Context): MyComponents = new MyComponents(context) } trait OneServerPerTestWithMyComponents - extends OneServerPerSuiteWithComponents[MyComponents] { + extends OneServerPerTestWithComponents[MyComponents] { this: Suite => override def createComponents(context: Context): MyComponents = new MyComponents(context) diff --git a/test/SeleniumSpec.scala b/test/SeleniumSpec.scala index 6d7c245db..21ae302c4 100644 --- a/test/SeleniumSpec.scala +++ b/test/SeleniumSpec.scala @@ -2,7 +2,7 @@ import org.scalatestplus.play._ class SeleniumSpec extends PlaySpec - with OneServerPerTest + with OneServerPerTestWithMyComponents with OneBrowserPerTest with HtmlUnitFactory { From 5ed13084759688765b4758081f33bf7ff726162d Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Tue, 26 Apr 2016 15:09:21 -0700 Subject: [PATCH 06/66] Add mixed play spec --- app/MyApplicationLoader.scala | 19 ++ test/MixedFixturesWithNoDefaultApp.scala | 326 +++++++++++++++++++++++ test/MixedPlaySpecWithNoDefaultApp.scala | 23 ++ test/RoutesSpec.scala | 12 + 4 files changed, 380 insertions(+) create mode 100644 test/MixedFixturesWithNoDefaultApp.scala create mode 100644 test/MixedPlaySpecWithNoDefaultApp.scala create mode 100644 test/RoutesSpec.scala diff --git a/app/MyApplicationLoader.scala b/app/MyApplicationLoader.scala index 317d5c120..74f246438 100644 --- a/app/MyApplicationLoader.scala +++ b/app/MyApplicationLoader.scala @@ -4,6 +4,7 @@ import play.api.inject._ import play.api.libs.ws.ahc.AhcWSComponents import play.api.routing.Router import router.Routes +import play.core.DefaultWebCommands class MyApplicationLoader extends ApplicationLoader { def load(context: ApplicationLoader.Context) = { @@ -14,6 +15,24 @@ class MyApplicationLoader extends ApplicationLoader { } } +/** + * An application builder for running an application in tests + */ +class MyApplicationBuilder { + + def build(): Application = { + val env = Environment.simple() + val context = new ApplicationLoader.Context( + environment = env, + sourceMapper = None, + webCommands = new DefaultWebCommands(), + initialConfiguration = Configuration.load(env) + ) + val loader = new MyApplicationLoader() + loader.load(context) + } +} + class MyComponents(context: ApplicationLoader.Context) extends BuiltInComponentsFromContext(context) with I18nComponents diff --git a/test/MixedFixturesWithNoDefaultApp.scala b/test/MixedFixturesWithNoDefaultApp.scala new file mode 100644 index 000000000..48e3b9e7f --- /dev/null +++ b/test/MixedFixturesWithNoDefaultApp.scala @@ -0,0 +1,326 @@ +/* + * Copyright 2001-2014 Artima, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file 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. + */ + +import org.scalatestplus.play._ + +import play.api.Application +import play.api.test._ +import org.scalatest._ + +import org.scalatest.fixture._ +import org.scalatest.selenium.WebBrowser +import org.openqa.selenium.WebDriver +import org.openqa.selenium.firefox.FirefoxDriver +import org.openqa.selenium.firefox.FirefoxProfile +import org.openqa.selenium.htmlunit.HtmlUnitDriver +import org.openqa.selenium.safari.SafariDriver +import org.openqa.selenium.chrome.ChromeDriver +import org.openqa.selenium.ie.InternetExplorerDriver +import org.scalatestplus.play.BrowserFactory.UnavailableDriver +import org.openqa.selenium.safari.SafariDriver + +trait MixedFixturesWithNoDefaultApp extends SuiteMixin with UnitFixture { this: fixture.Suite => + + /** + * `NoArg` subclass that provides an `Application` fixture. + */ + abstract class App(appFun: => Application) extends NoArg { + /** + * Makes the passed-in `Application` implicit. + */ + implicit def implicitApp: Application = app + + /** + * The lazy instance created from passed appFun + */ + lazy val app = appFun + + /** + * Runs the passed in `Application` before executing the test body, ensuring it is closed after the test body completes. + */ + override def apply() { + def callSuper = super.apply() // this is needed for Scala 2.10 to work + Helpers.running(app)(callSuper) + } + } + + /** + * `NoArg` subclass that provides a fixture composed of a `Application` and running `TestServer`. + */ + abstract class Server(appFun: => Application, val port: Int = Helpers.testServerPort) extends NoArg { + /** + * Makes the passed in `Application` implicit. + */ + implicit def implicitApp: Application = app + + /** + * The lazy instance created from passed appFun + */ + lazy val app = appFun + + /** + * Implicit `PortNumber` instance that wraps `port`. The value returned from `portNumber.value` + * will be same as the value of `port`. + */ + implicit lazy val portNumber: PortNumber = PortNumber(port) + + /** + * Runs a `TestServer` using the passed-in `Application` and port before executing the + * test body, ensuring both are stopped after the test body completes. + */ + override def apply() { + def callSuper = super.apply() // this is needed for Scala 2.10 to work + Helpers.running(TestServer(port, app))(callSuper) + } + } + + /** + * `NoArg` subclass that provides a fixture composed of an `Application`, running `TestServer`, and + * Selenium `HtmlUnitDriver`. + */ + abstract class HtmlUnit(appFun: => Application, val port: Int = Helpers.testServerPort) extends WebBrowser with fixture.NoArg with HtmlUnitFactory { + /** + * A lazy implicit instance of `HtmlUnitDriver`. It will hold `UnavailableDriver` if `HtmlUnitDriver` + * is not available in the running machine. + */ + implicit lazy val webDriver: WebDriver = createWebDriver() + + /** + * Makes the passed in `Application` implicit. + */ + implicit def implicitApp: Application = app + + /** + * The lazy instance created from passed appFun + */ + lazy val app = appFun + + /** + * Implicit `PortNumber` instance that wraps `port`. The value returned from `portNumber.value` + * will be same as the value of `port`. + */ + implicit lazy val portNumber: PortNumber = PortNumber(port) + + /** + * Runs a `TestServer` using the passed-in `Application` and port before executing the + * test body, which can use the `HtmlUnitDriver` provided by `webDriver`, ensuring all + * are are stopped after the test body completes. + */ + override def apply() { + webDriver match { + case UnavailableDriver(ex, errorMessage) => + ex match { + case Some(e) => cancel(errorMessage, e) + case None => cancel(errorMessage) + } + case _ => + def callSuper = super.apply() // this is needed for Scala 2.10 to work + try Helpers.running(TestServer(port, app))(callSuper) + finally webDriver.close() + } + } + } + + /** + * `NoArg` subclass that provides a fixture composed of a `Application`, running `TestServer`, and + * Selenium `FirefoxDriver`. + */ + abstract class Firefox(appFun: => Application, val port: Int = Helpers.testServerPort) extends WebBrowser with NoArg with FirefoxFactory { + + /** + * A lazy implicit instance of `FirefoxDriver`, it will hold `UnavailableDriver` if `FirefoxDriver` + * is not available in the running machine. + */ + implicit lazy val webDriver: WebDriver = createWebDriver() + + /** + * Makes the passed in `Application` implicit. + */ + implicit def implicitApp: Application = app + + /** + * The lazy instance created from passed appFun + */ + lazy val app = appFun + + /** + * Implicit `PortNumber` instance that wraps `port`. The value returned from `portNumber.value` + * will be same as the value of `port`. + */ + implicit lazy val portNumber: PortNumber = PortNumber(port) + + /** + * Runs a `TestServer` using the passed-in `Application` and port before executing the + * test body, which can use the `FirefoxDriver` provided by `webDriver`, ensuring all + * are are stopped after the test body completes. + */ + override def apply() { + webDriver match { + case UnavailableDriver(ex, errorMessage) => + ex match { + case Some(e) => cancel(errorMessage, e) + case None => cancel(errorMessage) + } + case _ => + def callSuper = super.apply() // this is needed for Scala 2.10 to work + try Helpers.running(TestServer(port, app))(callSuper) + finally webDriver.close() + } + } + } + + /** + * `NoArg` subclass that provides a fixture composed of an `Application`, running `TestServer`, and + * Selenium `SafariDriver`. + */ + abstract class Safari(appFun: => Application, val port: Int = Helpers.testServerPort) extends WebBrowser with NoArg with SafariFactory { + /** + * A lazy implicit instance of `SafariDriver`, it will hold `UnavailableDriver` if `SafariDriver` + * is not available in the running machine. + */ + implicit lazy val webDriver: WebDriver = createWebDriver() + + /** + * Makes the passed in `Application` implicit. + */ + implicit def implicitApp: Application = app + + /** + * The lazy instance created from passed appFun + */ + lazy val app = appFun + + /** + * Implicit `PortNumber` instance that wraps `port`. The value returned from `portNumber.value` + * will be same as the value of `port`. + */ + implicit lazy val portNumber: PortNumber = PortNumber(port) + + /** + * Runs a `TestServer` using the passed-in `Application` and port before executing the + * test body, which can use the `SafariDriver` provided by `webDriver`, ensuring all + * are are stopped after the test body completes. + */ + override def apply() { + webDriver match { + case UnavailableDriver(ex, errorMessage) => + ex match { + case Some(e) => cancel(errorMessage, e) + case None => cancel(errorMessage) + } + case _ => + def callSuper = super.apply() // this is needed for Scala 2.10 to work + try Helpers.running(TestServer(port, app))(callSuper) + finally webDriver.quit() + } + } + } + + /** + * `NoArg` subclass that provides a fixture composed of an `Application`, running `TestServer`, and + * Selenium `ChromeDriver`. + */ + abstract class Chrome(appFun: => Application, val port: Int = Helpers.testServerPort) extends WebBrowser with NoArg with ChromeFactory { + /** + * A lazy implicit instance of `ChromeDriver`, it will hold `UnavailableDriver` if `ChromeDriver` + * is not available in the running machine. + */ + implicit lazy val webDriver: WebDriver = createWebDriver() + + /** + * Makes the passed in `Application` implicit. + */ + implicit def implicitApp: Application = app + + /** + * The lazy instance created from passed appFun + */ + lazy val app = appFun + + /** + * Implicit `PortNumber` instance that wraps `port`. The value returned from `portNumber.value` + * will be same as the value of `port`. + */ + implicit lazy val portNumber: PortNumber = PortNumber(port) + + /** + * Runs a `TestServer` using the passed-in `Application` and port before executing the + * test body, which can use the `ChromeDriver` provided by `webDriver`, ensuring all + * are are stopped after the test body completes. + */ + override def apply() { + webDriver match { + case UnavailableDriver(ex, errorMessage) => + ex match { + case Some(e) => cancel(errorMessage, e) + case None => cancel(errorMessage) + } + case _ => + def callSuper = super.apply() // this is needed for Scala 2.10 to work + try Helpers.running(TestServer(port, app))(callSuper) + finally webDriver.quit() + } + } + } + + /** + * `NoArg` subclass that provides a fixture composed of an `Application`, running `TestServer`, and + * Selenium `InternetExplorerDriver`. + */ + abstract class InternetExplorer(appFun: => Application, val port: Int = Helpers.testServerPort) extends WebBrowser with NoArg with InternetExplorerFactory { + /** + * A lazy implicit instance of `InternetExplorerDriver`, it will hold `UnavailableDriver` if `InternetExplorerDriver` + * is not available in the running machine. + */ + implicit lazy val webDriver: WebDriver = createWebDriver() + + /** + * Makes the passed in `Application` implicit. + */ + implicit def implicitApp: Application = app + + /** + * The lazy instance created from passed appFun + */ + lazy val app = appFun + + /** + * Implicit `PortNumber` instance that wraps `port`. The value returned from `portNumber.value` + * will be same as the value of `port`. + */ + implicit lazy val portNumber: PortNumber = PortNumber(port) + + /** + * Runs a `TestServer` using the passed-in `Application` and port before executing the + * test body, which can use the `InternetExplorerDriver` provided by `webDriver`, ensuring all + * are are stopped after the test body completes. + */ + override def apply() { + webDriver match { + case UnavailableDriver(ex, errorMessage) => + ex match { + case Some(e) => cancel(errorMessage, e) + case None => cancel(errorMessage) + } + case _ => + def callSuper = super.apply() // this is needed for Scala 2.10 to work + try Helpers.running(TestServer(port, app))(callSuper) + finally webDriver.close() + } + } + } +} + diff --git a/test/MixedPlaySpecWithNoDefaultApp.scala b/test/MixedPlaySpecWithNoDefaultApp.scala new file mode 100644 index 000000000..48a86526b --- /dev/null +++ b/test/MixedPlaySpecWithNoDefaultApp.scala @@ -0,0 +1,23 @@ +import org.scalatest.concurrent.{Eventually, IntegrationPatience} +import org.scalatest.{MustMatchers, OptionValues, fixture} +import org.scalatestplus.play.{PortNumber, WsScalaTestClient} +import play.api.libs.ws.{WSClient, WSRequest} +import play.api.mvc.Call + +abstract class MixedPlaySpecWithNoDefaultApp extends fixture.WordSpec + with MustMatchers + with OptionValues + with MixedFixturesWithNoDefaultApp + with Eventually + with IntegrationPatience + with WsScalaTestClient +{ + + //def wsCall(call: Call)(implicit portNumber: PortNumber, wsClient: WSClient): WSRequest = doCall(call.url, wsClient, portNumber) + + // def wsUrl(url: String)(implicit portNumber: PortNumber, wsClient: WSClient): WSRequest = doCall(url, wsClient, portNumber) + + //private def doCall(url: String, wsClient: WSClient, portNumber: PortNumber) = { + // wsClient.url("http://localhost:" + portNumber.value + url) + //} +} diff --git a/test/RoutesSpec.scala b/test/RoutesSpec.scala new file mode 100644 index 000000000..84ec7b375 --- /dev/null +++ b/test/RoutesSpec.scala @@ -0,0 +1,12 @@ + +class RoutesSpec extends MixedPlaySpecWithNoDefaultApp +{ + "send OK on router test" in new Server((new MyApplicationBuilder()).build()) { + implicit val ec = app.materializer.executionContext + + wsUrl("/").get().map { response => + response.status mustBe 200 + } + } + +} \ No newline at end of file From 6cdbab40ecae1f372873c687318179290b370310 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Tue, 17 May 2016 12:10:10 -0700 Subject: [PATCH 07/66] Create .travis.yml --- .travis.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..aaa98c8dd --- /dev/null +++ b/.travis.yml @@ -0,0 +1,14 @@ +language: scala +scala: + - 2.11.8 +jdk: + - oraclejdk8 +cache: + directories: + - $HOME/.ivy2/cache +before_cache: + # Ensure changes to the cache aren't persisted + - rm -rf $HOME/.ivy2/cache/com.typesafe.play/* + - rm -rf $HOME/.ivy2/cache/scala_*/sbt_*/com.typesafe.play/* + # Delete all ivydata files since ivy touches them on each build + - find $HOME/.ivy2/cache -name "ivydata-*.properties" -print0 | xargs -n10 -0 rm From fd2dad0cd4cf36ed051902795ab40d162d63e36d Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Tue, 17 May 2016 12:22:07 -0700 Subject: [PATCH 08/66] Upgrade to Play 2.5.3 --- build.sbt | 2 +- project/plugins.sbt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index 9399c8189..104aaebee 100644 --- a/build.sbt +++ b/build.sbt @@ -4,7 +4,7 @@ version := "1.0-SNAPSHOT" lazy val root = (project in file(".")).enablePlugins(PlayScala) -scalaVersion := "2.11.7" +scalaVersion := "2.11.8" libraryDependencies ++= Seq( ws, diff --git a/project/plugins.sbt b/project/plugins.sbt index 1f45b0514..285986ea8 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,2 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.5.2") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.5.3") From 2338c9c5495e7ff5b3f04511603e222524aa23b1 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Tue, 17 May 2016 12:45:14 -0700 Subject: [PATCH 09/66] Add readme --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e145890fa..620aac8a1 100644 --- a/README.md +++ b/README.md @@ -122,4 +122,10 @@ class ServerSpec extends PlaySpec } ``` -Please refer to https://www.playframework.com/documentation/2.5.x/ScalaCompileTimeDependencyInjection and https://www.playframework.com/documentation/2.5.x/ScalaTestingWithScalaTest#Using-ScalaTest-+-Play for documentation. +## Further Documentation + +* https://www.playframework.com/documentation/2.5.x/ScalaCompileTimeDependencyInjection +* https://www.playframework.com/documentation/2.5.x/ScalaTestingWithScalaTest#Using-ScalaTest-+-Play +* http://www.scalatest.org/user_guide +* http://www.scalatest.org/release_notes/3.0.0 +* https://github.com/playframework/scalatestplus-play From 62dc758524ecb01288f2489fbe19e862c7389eaf Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Wed, 5 Oct 2016 22:49:49 -0500 Subject: [PATCH 10/66] add travis --- .travis.yml | 17 +++++++++-------- build.sbt | 8 +++----- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index aaa98c8dd..cfe871c2a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,15 @@ language: scala scala: - - 2.11.8 +- 2.11.8 jdk: - - oraclejdk8 +- oraclejdk8 cache: directories: - - $HOME/.ivy2/cache + - "$HOME/.ivy2/cache" before_cache: - # Ensure changes to the cache aren't persisted - - rm -rf $HOME/.ivy2/cache/com.typesafe.play/* - - rm -rf $HOME/.ivy2/cache/scala_*/sbt_*/com.typesafe.play/* - # Delete all ivydata files since ivy touches them on each build - - find $HOME/.ivy2/cache -name "ivydata-*.properties" -print0 | xargs -n10 -0 rm +- 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: e2qJtRrHD8Mb9GhZXXeVzfxwZjRgKBq1FUv825gXB+iRm1Vn2J+Ysk3Hv7xrANxo2A6JXkPMtZG8wyzcYDcKrRX7CxMYbytlrqI0+7v8VicdO8wDerplvROmWoYpKOFmxza0wz3ENL8IEo2pkD8baPWYty9a+GEIGwuyKsuqGNEKzW7JHtWZXdKE8gEQKneNxErtFclI4yQqHk0NHJu+8/+BViNQ1PzaRAIEOj690x2ls9x2joEIQzSUeMdNBbjc5Xi45dDPgjb5jYwwjLRqChSOrEDOdF6cPpSYFXENizVd85LaoES3iL+1J8lG7HBARCvtASn+5KiXwVUtPCoiabqE4jmTkhi124PLU17fnhJSboPn5G8FrjoCYPhzSOFzWAkwAIsGBKbL4pC+sqh/HFbh/k/YwyksBZIrWpu0BfgVpCxqaf/1fM5Yj12yth1QLrDz5dOSoeJN2jovKyD9UAJlhq+SOBKpYBE3g6TTcRPFqNxY0gSZPCXP25XTxExWX1ZV76zAjh8gJcTpBzpqyOAyuylrfRfSnS8TrM81xeh49XnJMqSDHsDBnjiWKyAp4xtVG1Rch4akNcfBPf7LKL4Ve2JM7rRWijw4cIRBX1VpZ4SRwafdNLi4xIZimtVnye+nk9K1DgwmItls78mACWOKIzGsPf+DI6NALmU2M/8= diff --git a/build.sbt b/build.sbt index 104aaebee..a34952113 100644 --- a/build.sbt +++ b/build.sbt @@ -1,4 +1,4 @@ -name := """play-2.5.x-scala-compile-di-with-tests""" +name := """play-scala-compile-di-with-tests""" version := "1.0-SNAPSHOT" @@ -6,9 +6,7 @@ lazy val root = (project in file(".")).enablePlugins(PlayScala) scalaVersion := "2.11.8" -libraryDependencies ++= Seq( - ws, - "org.scalatestplus.play" %% "scalatestplus-play" % "1.5.1" % Test -) +libraryDependencies += ws +libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "1.5.1" % Test resolvers += "scalaz-bintray" at "http://dl.bintray.com/scalaz/releases" From 47699470614f93b9bce71a5e3cb0be7d83eca9d7 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Wed, 5 Oct 2016 23:07:40 -0500 Subject: [PATCH 11/66] add snapshot --- build.sbt | 1 + project/plugins.sbt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index a34952113..5bed8e678 100644 --- a/build.sbt +++ b/build.sbt @@ -6,6 +6,7 @@ lazy val root = (project in file(".")).enablePlugins(PlayScala) scalaVersion := "2.11.8" +libraryDependencies += guice libraryDependencies += ws libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "1.5.1" % Test diff --git a/project/plugins.sbt b/project/plugins.sbt index 285986ea8..c2a9eb66b 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,2 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.5.3") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-SNAPSHOT") From f874962541c21a7962feefc9ab9a42d5c9cc46f2 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Thu, 16 Feb 2017 21:59:28 -0800 Subject: [PATCH 12/66] Updated with template-control on 2017-02-17T05:59:28Z /LICENSE: wrote /LICENSE **/build.sbt: scalaVersion := "2.12.1" **/build.sbt: libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "2.0.0-M2" % Test **/build.properties: sbt.version=0.13.13 **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-M1") --- LICENSE | 11 +++++------ build.sbt | 4 ++-- conf/application.conf | 2 +- project/build.properties | 2 +- project/plugins.sbt | 2 +- 5 files changed, 10 insertions(+), 11 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 5bed8e678..96ce6a769 100644 --- a/build.sbt +++ b/build.sbt @@ -4,10 +4,10 @@ version := "1.0-SNAPSHOT" lazy val root = (project in file(".")).enablePlugins(PlayScala) -scalaVersion := "2.11.8" +scalaVersion := "2.12.1" libraryDependencies += guice libraryDependencies += ws -libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "1.5.1" % Test +libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "2.0.0-M2" % Test resolvers += "scalaz-bintray" at "http://dl.bintray.com/scalaz/releases" diff --git a/conf/application.conf b/conf/application.conf index 397194ccf..50f8f9d3e 100644 --- a/conf/application.conf +++ b/conf/application.conf @@ -1,2 +1,2 @@ -play.application.loader=MyApplicationLoader \ No newline at end of file +play.application.loader=MyApplicationLoader diff --git a/project/build.properties b/project/build.properties index 43b8278c6..27e88aa11 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=0.13.11 +sbt.version=0.13.13 diff --git a/project/plugins.sbt b/project/plugins.sbt index c2a9eb66b..8292bc8ad 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,2 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-SNAPSHOT") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-M1") From 34bb04919ac4c7af73eafdb86081e3862a0d402a Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Fri, 17 Feb 2017 09:26:23 -0800 Subject: [PATCH 13/66] Upgrade to Play 2.6.0-M1 --- .travis.yml | 1 + app/MyApplicationLoader.scala | 29 +- app/controllers/HomeController.scala | 4 +- build.sbt | 4 +- project/build.properties | 2 +- project/plugins.sbt | 2 +- test/ApplicationSpec.scala | 17 +- test/CompileTimeComponents.scala | 30 --- test/MixedFixturesWithNoDefaultApp.scala | 326 ----------------------- test/MixedPlaySpecWithNoDefaultApp.scala | 23 -- test/MyApplicationFactory.scala | 8 + test/RoutesSpec.scala | 12 - test/ScalaTestWithComponents.scala | 35 --- test/SeleniumSpec.scala | 12 +- test/ServerSpec.scala | 21 +- test/WithApplicationComponents.scala | 24 -- 16 files changed, 55 insertions(+), 495 deletions(-) delete mode 100644 test/CompileTimeComponents.scala delete mode 100644 test/MixedFixturesWithNoDefaultApp.scala delete mode 100644 test/MixedPlaySpecWithNoDefaultApp.scala create mode 100644 test/MyApplicationFactory.scala delete mode 100644 test/RoutesSpec.scala delete mode 100644 test/ScalaTestWithComponents.scala delete mode 100644 test/WithApplicationComponents.scala diff --git a/.travis.yml b/.travis.yml index cfe871c2a..b63e07068 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,7 @@ language: scala scala: - 2.11.8 +- 2.12.1 jdk: - oraclejdk8 cache: diff --git a/app/MyApplicationLoader.scala b/app/MyApplicationLoader.scala index 74f246438..a3f75b460 100644 --- a/app/MyApplicationLoader.scala +++ b/app/MyApplicationLoader.scala @@ -1,9 +1,8 @@ import play.api._ -import play.api.i18n._ import play.api.inject._ -import play.api.libs.ws.ahc.AhcWSComponents +import play.api.libs.ws.ahc._ +import play.api.mvc._ import play.api.routing.Router -import router.Routes import play.core.DefaultWebCommands class MyApplicationLoader extends ApplicationLoader { @@ -22,11 +21,12 @@ class MyApplicationBuilder { def build(): Application = { val env = Environment.simple() - val context = new ApplicationLoader.Context( + val context = ApplicationLoader.Context( environment = env, sourceMapper = None, webCommands = new DefaultWebCommands(), - initialConfiguration = Configuration.load(env) + initialConfiguration = Configuration.load(env), + lifecycle = new DefaultApplicationLifecycle() ) val loader = new MyApplicationLoader() loader.load(context) @@ -35,8 +35,13 @@ class MyApplicationBuilder { class MyComponents(context: ApplicationLoader.Context) extends BuiltInComponentsFromContext(context) - with I18nComponents - with AhcWSComponents { + with AhcWSComponents + with _root_.controllers.AssetsComponents + with ControllerComponents { + + lazy val parsers: PlayBodyParsers = PlayBodyParsers(httpConfiguration.parser, httpErrorHandler, materializer, tempFileCreator) + + lazy val actionBuilder: ActionBuilder[Request, AnyContent] = DefaultActionBuilder(parsers.anyContent) override lazy val injector = { new SimpleInjector(NewInstanceInjector) + @@ -45,14 +50,12 @@ class MyComponents(context: ApplicationLoader.Context) csrfTokenSigner + httpConfiguration + tempFileCreator + - global + - crypto + - wsApi + + wsClient + messagesApi } - lazy val router: Router = new Routes(httpErrorHandler, homeController, assets) + lazy val homeController = new _root_.controllers.HomeController(this) + + lazy val router: Router = new _root_.router.Routes(httpErrorHandler, homeController, assets) - lazy val homeController = new controllers.HomeController() - lazy val assets = new controllers.Assets(httpErrorHandler) } diff --git a/app/controllers/HomeController.scala b/app/controllers/HomeController.scala index 34debb1a6..474c1817a 100644 --- a/app/controllers/HomeController.scala +++ b/app/controllers/HomeController.scala @@ -1,5 +1,7 @@ package controllers +import javax.inject.Inject + import play.api.mvc._ /** @@ -7,7 +9,7 @@ import play.api.mvc._ * application's home page. */ -class HomeController extends Controller { +class HomeController @Inject()(cc: ControllerComponents) extends AbstractController(cc) { /** * Create an Action to render an HTML page with a welcome message. diff --git a/build.sbt b/build.sbt index 5bed8e678..96ce6a769 100644 --- a/build.sbt +++ b/build.sbt @@ -4,10 +4,10 @@ version := "1.0-SNAPSHOT" lazy val root = (project in file(".")).enablePlugins(PlayScala) -scalaVersion := "2.11.8" +scalaVersion := "2.12.1" libraryDependencies += guice libraryDependencies += ws -libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "1.5.1" % Test +libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "2.0.0-M2" % Test resolvers += "scalaz-bintray" at "http://dl.bintray.com/scalaz/releases" 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 c2a9eb66b..8292bc8ad 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,2 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-SNAPSHOT") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-M1") diff --git a/test/ApplicationSpec.scala b/test/ApplicationSpec.scala index 0c9b40b0d..1cb78001c 100644 --- a/test/ApplicationSpec.scala +++ b/test/ApplicationSpec.scala @@ -1,27 +1,21 @@ -import org.scalatest._ import org.scalatestplus.play._ -import play.api.ApplicationLoader.Context -import play.api.test._ import play.api.test.Helpers._ -import play.api._ +import play.api.test._ /** - * Add your spec here. - * You can mock out a whole application including requests, plugins etc. - * For more information, consult the wiki. + * Runs an integration test with an application */ -class ApplicationSpec extends PlaySpec with OneAppPerTestWithMyComponents { +class ApplicationSpec extends PlaySpec + with BaseOneAppPerTest + with MyApplicationFactory { "Routes" should { - "send 404 on a bad request" in { route(app, FakeRequest(GET, "/boum")).map(status(_)) mustBe Some(NOT_FOUND) } - } "HomeController" should { - "render the index page" in { val home = route(app, FakeRequest(GET, "/")).get @@ -29,6 +23,5 @@ class ApplicationSpec extends PlaySpec with OneAppPerTestWithMyComponents { contentType(home) mustBe Some("text/html") contentAsString(home) must include("Your new application is ready.") } - } } diff --git a/test/CompileTimeComponents.scala b/test/CompileTimeComponents.scala deleted file mode 100644 index 14c173df4..000000000 --- a/test/CompileTimeComponents.scala +++ /dev/null @@ -1,30 +0,0 @@ -import org.scalatest.Suite -import play.api.ApplicationLoader.Context - -trait OneAppPerTestWithMyComponents - extends OneAppPerTestWithComponents[MyComponents] { - this: Suite => - - override def createComponents(context: Context): MyComponents = new MyComponents(context) -} - -trait OneAppPerSuiteWithMyComponents - extends OneAppPerSuiteWithComponents[MyComponents] { - this: Suite => - - override def createComponents(context: Context): MyComponents = new MyComponents(context) -} - -trait OneServerPerTestWithMyComponents - extends OneServerPerTestWithComponents[MyComponents] { - this: Suite => - - override def createComponents(context: Context): MyComponents = new MyComponents(context) -} - -trait OneServerPerSuiteWithMyComponents - extends OneServerPerSuiteWithComponents[MyComponents] { - this: Suite => - - override def createComponents(context: Context): MyComponents = new MyComponents(context) -} diff --git a/test/MixedFixturesWithNoDefaultApp.scala b/test/MixedFixturesWithNoDefaultApp.scala deleted file mode 100644 index 48e3b9e7f..000000000 --- a/test/MixedFixturesWithNoDefaultApp.scala +++ /dev/null @@ -1,326 +0,0 @@ -/* - * Copyright 2001-2014 Artima, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file 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. - */ - -import org.scalatestplus.play._ - -import play.api.Application -import play.api.test._ -import org.scalatest._ - -import org.scalatest.fixture._ -import org.scalatest.selenium.WebBrowser -import org.openqa.selenium.WebDriver -import org.openqa.selenium.firefox.FirefoxDriver -import org.openqa.selenium.firefox.FirefoxProfile -import org.openqa.selenium.htmlunit.HtmlUnitDriver -import org.openqa.selenium.safari.SafariDriver -import org.openqa.selenium.chrome.ChromeDriver -import org.openqa.selenium.ie.InternetExplorerDriver -import org.scalatestplus.play.BrowserFactory.UnavailableDriver -import org.openqa.selenium.safari.SafariDriver - -trait MixedFixturesWithNoDefaultApp extends SuiteMixin with UnitFixture { this: fixture.Suite => - - /** - * `NoArg` subclass that provides an `Application` fixture. - */ - abstract class App(appFun: => Application) extends NoArg { - /** - * Makes the passed-in `Application` implicit. - */ - implicit def implicitApp: Application = app - - /** - * The lazy instance created from passed appFun - */ - lazy val app = appFun - - /** - * Runs the passed in `Application` before executing the test body, ensuring it is closed after the test body completes. - */ - override def apply() { - def callSuper = super.apply() // this is needed for Scala 2.10 to work - Helpers.running(app)(callSuper) - } - } - - /** - * `NoArg` subclass that provides a fixture composed of a `Application` and running `TestServer`. - */ - abstract class Server(appFun: => Application, val port: Int = Helpers.testServerPort) extends NoArg { - /** - * Makes the passed in `Application` implicit. - */ - implicit def implicitApp: Application = app - - /** - * The lazy instance created from passed appFun - */ - lazy val app = appFun - - /** - * Implicit `PortNumber` instance that wraps `port`. The value returned from `portNumber.value` - * will be same as the value of `port`. - */ - implicit lazy val portNumber: PortNumber = PortNumber(port) - - /** - * Runs a `TestServer` using the passed-in `Application` and port before executing the - * test body, ensuring both are stopped after the test body completes. - */ - override def apply() { - def callSuper = super.apply() // this is needed for Scala 2.10 to work - Helpers.running(TestServer(port, app))(callSuper) - } - } - - /** - * `NoArg` subclass that provides a fixture composed of an `Application`, running `TestServer`, and - * Selenium `HtmlUnitDriver`. - */ - abstract class HtmlUnit(appFun: => Application, val port: Int = Helpers.testServerPort) extends WebBrowser with fixture.NoArg with HtmlUnitFactory { - /** - * A lazy implicit instance of `HtmlUnitDriver`. It will hold `UnavailableDriver` if `HtmlUnitDriver` - * is not available in the running machine. - */ - implicit lazy val webDriver: WebDriver = createWebDriver() - - /** - * Makes the passed in `Application` implicit. - */ - implicit def implicitApp: Application = app - - /** - * The lazy instance created from passed appFun - */ - lazy val app = appFun - - /** - * Implicit `PortNumber` instance that wraps `port`. The value returned from `portNumber.value` - * will be same as the value of `port`. - */ - implicit lazy val portNumber: PortNumber = PortNumber(port) - - /** - * Runs a `TestServer` using the passed-in `Application` and port before executing the - * test body, which can use the `HtmlUnitDriver` provided by `webDriver`, ensuring all - * are are stopped after the test body completes. - */ - override def apply() { - webDriver match { - case UnavailableDriver(ex, errorMessage) => - ex match { - case Some(e) => cancel(errorMessage, e) - case None => cancel(errorMessage) - } - case _ => - def callSuper = super.apply() // this is needed for Scala 2.10 to work - try Helpers.running(TestServer(port, app))(callSuper) - finally webDriver.close() - } - } - } - - /** - * `NoArg` subclass that provides a fixture composed of a `Application`, running `TestServer`, and - * Selenium `FirefoxDriver`. - */ - abstract class Firefox(appFun: => Application, val port: Int = Helpers.testServerPort) extends WebBrowser with NoArg with FirefoxFactory { - - /** - * A lazy implicit instance of `FirefoxDriver`, it will hold `UnavailableDriver` if `FirefoxDriver` - * is not available in the running machine. - */ - implicit lazy val webDriver: WebDriver = createWebDriver() - - /** - * Makes the passed in `Application` implicit. - */ - implicit def implicitApp: Application = app - - /** - * The lazy instance created from passed appFun - */ - lazy val app = appFun - - /** - * Implicit `PortNumber` instance that wraps `port`. The value returned from `portNumber.value` - * will be same as the value of `port`. - */ - implicit lazy val portNumber: PortNumber = PortNumber(port) - - /** - * Runs a `TestServer` using the passed-in `Application` and port before executing the - * test body, which can use the `FirefoxDriver` provided by `webDriver`, ensuring all - * are are stopped after the test body completes. - */ - override def apply() { - webDriver match { - case UnavailableDriver(ex, errorMessage) => - ex match { - case Some(e) => cancel(errorMessage, e) - case None => cancel(errorMessage) - } - case _ => - def callSuper = super.apply() // this is needed for Scala 2.10 to work - try Helpers.running(TestServer(port, app))(callSuper) - finally webDriver.close() - } - } - } - - /** - * `NoArg` subclass that provides a fixture composed of an `Application`, running `TestServer`, and - * Selenium `SafariDriver`. - */ - abstract class Safari(appFun: => Application, val port: Int = Helpers.testServerPort) extends WebBrowser with NoArg with SafariFactory { - /** - * A lazy implicit instance of `SafariDriver`, it will hold `UnavailableDriver` if `SafariDriver` - * is not available in the running machine. - */ - implicit lazy val webDriver: WebDriver = createWebDriver() - - /** - * Makes the passed in `Application` implicit. - */ - implicit def implicitApp: Application = app - - /** - * The lazy instance created from passed appFun - */ - lazy val app = appFun - - /** - * Implicit `PortNumber` instance that wraps `port`. The value returned from `portNumber.value` - * will be same as the value of `port`. - */ - implicit lazy val portNumber: PortNumber = PortNumber(port) - - /** - * Runs a `TestServer` using the passed-in `Application` and port before executing the - * test body, which can use the `SafariDriver` provided by `webDriver`, ensuring all - * are are stopped after the test body completes. - */ - override def apply() { - webDriver match { - case UnavailableDriver(ex, errorMessage) => - ex match { - case Some(e) => cancel(errorMessage, e) - case None => cancel(errorMessage) - } - case _ => - def callSuper = super.apply() // this is needed for Scala 2.10 to work - try Helpers.running(TestServer(port, app))(callSuper) - finally webDriver.quit() - } - } - } - - /** - * `NoArg` subclass that provides a fixture composed of an `Application`, running `TestServer`, and - * Selenium `ChromeDriver`. - */ - abstract class Chrome(appFun: => Application, val port: Int = Helpers.testServerPort) extends WebBrowser with NoArg with ChromeFactory { - /** - * A lazy implicit instance of `ChromeDriver`, it will hold `UnavailableDriver` if `ChromeDriver` - * is not available in the running machine. - */ - implicit lazy val webDriver: WebDriver = createWebDriver() - - /** - * Makes the passed in `Application` implicit. - */ - implicit def implicitApp: Application = app - - /** - * The lazy instance created from passed appFun - */ - lazy val app = appFun - - /** - * Implicit `PortNumber` instance that wraps `port`. The value returned from `portNumber.value` - * will be same as the value of `port`. - */ - implicit lazy val portNumber: PortNumber = PortNumber(port) - - /** - * Runs a `TestServer` using the passed-in `Application` and port before executing the - * test body, which can use the `ChromeDriver` provided by `webDriver`, ensuring all - * are are stopped after the test body completes. - */ - override def apply() { - webDriver match { - case UnavailableDriver(ex, errorMessage) => - ex match { - case Some(e) => cancel(errorMessage, e) - case None => cancel(errorMessage) - } - case _ => - def callSuper = super.apply() // this is needed for Scala 2.10 to work - try Helpers.running(TestServer(port, app))(callSuper) - finally webDriver.quit() - } - } - } - - /** - * `NoArg` subclass that provides a fixture composed of an `Application`, running `TestServer`, and - * Selenium `InternetExplorerDriver`. - */ - abstract class InternetExplorer(appFun: => Application, val port: Int = Helpers.testServerPort) extends WebBrowser with NoArg with InternetExplorerFactory { - /** - * A lazy implicit instance of `InternetExplorerDriver`, it will hold `UnavailableDriver` if `InternetExplorerDriver` - * is not available in the running machine. - */ - implicit lazy val webDriver: WebDriver = createWebDriver() - - /** - * Makes the passed in `Application` implicit. - */ - implicit def implicitApp: Application = app - - /** - * The lazy instance created from passed appFun - */ - lazy val app = appFun - - /** - * Implicit `PortNumber` instance that wraps `port`. The value returned from `portNumber.value` - * will be same as the value of `port`. - */ - implicit lazy val portNumber: PortNumber = PortNumber(port) - - /** - * Runs a `TestServer` using the passed-in `Application` and port before executing the - * test body, which can use the `InternetExplorerDriver` provided by `webDriver`, ensuring all - * are are stopped after the test body completes. - */ - override def apply() { - webDriver match { - case UnavailableDriver(ex, errorMessage) => - ex match { - case Some(e) => cancel(errorMessage, e) - case None => cancel(errorMessage) - } - case _ => - def callSuper = super.apply() // this is needed for Scala 2.10 to work - try Helpers.running(TestServer(port, app))(callSuper) - finally webDriver.close() - } - } - } -} - diff --git a/test/MixedPlaySpecWithNoDefaultApp.scala b/test/MixedPlaySpecWithNoDefaultApp.scala deleted file mode 100644 index 48a86526b..000000000 --- a/test/MixedPlaySpecWithNoDefaultApp.scala +++ /dev/null @@ -1,23 +0,0 @@ -import org.scalatest.concurrent.{Eventually, IntegrationPatience} -import org.scalatest.{MustMatchers, OptionValues, fixture} -import org.scalatestplus.play.{PortNumber, WsScalaTestClient} -import play.api.libs.ws.{WSClient, WSRequest} -import play.api.mvc.Call - -abstract class MixedPlaySpecWithNoDefaultApp extends fixture.WordSpec - with MustMatchers - with OptionValues - with MixedFixturesWithNoDefaultApp - with Eventually - with IntegrationPatience - with WsScalaTestClient -{ - - //def wsCall(call: Call)(implicit portNumber: PortNumber, wsClient: WSClient): WSRequest = doCall(call.url, wsClient, portNumber) - - // def wsUrl(url: String)(implicit portNumber: PortNumber, wsClient: WSClient): WSRequest = doCall(url, wsClient, portNumber) - - //private def doCall(url: String, wsClient: WSClient, portNumber: PortNumber) = { - // wsClient.url("http://localhost:" + portNumber.value + url) - //} -} diff --git a/test/MyApplicationFactory.scala b/test/MyApplicationFactory.scala new file mode 100644 index 000000000..3e57cefcb --- /dev/null +++ b/test/MyApplicationFactory.scala @@ -0,0 +1,8 @@ +import org.scalatestplus.play.FakeApplicationFactory +import play.api.Application + +trait MyApplicationFactory extends FakeApplicationFactory { + override def fakeApplication(): Application = { + new MyApplicationBuilder().build() + } +} diff --git a/test/RoutesSpec.scala b/test/RoutesSpec.scala deleted file mode 100644 index 84ec7b375..000000000 --- a/test/RoutesSpec.scala +++ /dev/null @@ -1,12 +0,0 @@ - -class RoutesSpec extends MixedPlaySpecWithNoDefaultApp -{ - "send OK on router test" in new Server((new MyApplicationBuilder()).build()) { - implicit val ec = app.materializer.executionContext - - wsUrl("/").get().map { response => - response.status mustBe 200 - } - } - -} \ No newline at end of file diff --git a/test/ScalaTestWithComponents.scala b/test/ScalaTestWithComponents.scala deleted file mode 100644 index 1231bcc3a..000000000 --- a/test/ScalaTestWithComponents.scala +++ /dev/null @@ -1,35 +0,0 @@ -import org.scalatest.{Suite, TestData} -import org.scalatestplus.play.{OneAppPerSuite, OneAppPerTest, OneServerPerSuite, OneServerPerTest} -import play.api.{BuiltInComponents, _} - -trait OneAppPerTestWithComponents[T <: BuiltInComponents] - extends OneAppPerTest - with WithApplicationComponents[T] { - this: Suite => - - override def newAppForTest(testData: TestData): Application = newApplication -} - -trait OneAppPerSuiteWithComponents[T <: BuiltInComponents] - extends OneAppPerSuite - with WithApplicationComponents[T] { - this: Suite => - override implicit lazy val app: Application = newApplication -} - -trait OneServerPerTestWithComponents[T <: BuiltInComponents] - extends OneServerPerTest - with WithApplicationComponents[T] { - this: Suite => - - override def newAppForTest(testData: TestData): Application = newApplication -} - -trait OneServerPerSuiteWithComponents[T <: BuiltInComponents] - extends OneServerPerSuite - with WithApplicationComponents[T] { - this: Suite => - - override implicit lazy val app: Application = newApplication -} - diff --git a/test/SeleniumSpec.scala b/test/SeleniumSpec.scala index 21ae302c4..4b0c5fdbb 100644 --- a/test/SeleniumSpec.scala +++ b/test/SeleniumSpec.scala @@ -1,10 +1,10 @@ import org.scalatestplus.play._ -class SeleniumSpec - extends PlaySpec - with OneServerPerTestWithMyComponents - with OneBrowserPerTest - with HtmlUnitFactory { +class SeleniumSpec extends PlaySpec + with BaseOneServerPerTest + with OneBrowserPerTest + with MyApplicationFactory + with HtmlUnitFactory { "SeleniumSpec" should { @@ -12,7 +12,7 @@ class SeleniumSpec go to ("http://localhost:" + port) - pageSource must include ("Your new application is ready.") + pageSource must include("Your new application is ready.") } } } diff --git a/test/ServerSpec.scala b/test/ServerSpec.scala index 11ef7b770..e7b47fb4b 100644 --- a/test/ServerSpec.scala +++ b/test/ServerSpec.scala @@ -1,21 +1,24 @@ import org.scalatest.concurrent.ScalaFutures import org.scalatestplus.play._ +import play.api.libs.ws.WSClient import play.api.mvc.Results -import play.api.test.Helpers._ +import play.api.test.Injecting +/** + * Runs a play server on the default test port (Helpers.testServerPort == 19001). + */ class ServerSpec extends PlaySpec - with OneServerPerSuiteWithMyComponents - with Results + with BaseOneServerPerSuite + with MyApplicationFactory + with Injecting with ScalaFutures { - "Server query should" should { + implicit val wsClient: WSClient = inject[WSClient] + "Server query should" should { "work" in { - implicit val ec = app.materializer.executionContext - val wsClient = components.wsClient - - whenReady(wsUrl("/")(portNumber, wsClient).get) { response => - response.status mustBe OK + whenReady(wsUrl("/").get) { response => + response.status mustBe play.api.http.Status.OK } } diff --git a/test/WithApplicationComponents.scala b/test/WithApplicationComponents.scala deleted file mode 100644 index dd0115bf2..000000000 --- a/test/WithApplicationComponents.scala +++ /dev/null @@ -1,24 +0,0 @@ -import play.api.ApplicationLoader.Context -import play.api._ - -trait WithApplicationComponents[T <: BuiltInComponents] { - private var _components: T = _ - - // accessed to get the components in tests - final def components: T = _components - - // overridden by subclasses - def createComponents(context: Context): T - - // creates a new application and sets the components - def newApplication: Application = { - _components = createComponents(context) - _components.application - } - - def context: ApplicationLoader.Context = { - val classLoader = ApplicationLoader.getClass.getClassLoader - val env = new Environment(new java.io.File("."), classLoader, Mode.Test) - ApplicationLoader.createContext(env) - } -} From 5036f93e2a766499a8055dff35852cd15a4c5294 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Fri, 17 Feb 2017 15:40:54 -0800 Subject: [PATCH 14/66] Updated with template-control on 2017-02-17T23:40:54.707Z /LICENSE: wrote /LICENSE --- project/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 4b270a894f4836d520408a0cc0aad1e8bd00512f Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Tue, 21 Feb 2017 15:13:37 -0800 Subject: [PATCH 15/66] Use the direct hookup for ControllerComponents --- app/MyApplicationLoader.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/MyApplicationLoader.scala b/app/MyApplicationLoader.scala index a3f75b460..0c5aad646 100644 --- a/app/MyApplicationLoader.scala +++ b/app/MyApplicationLoader.scala @@ -39,9 +39,9 @@ class MyComponents(context: ApplicationLoader.Context) with _root_.controllers.AssetsComponents with ControllerComponents { - lazy val parsers: PlayBodyParsers = PlayBodyParsers(httpConfiguration.parser, httpErrorHandler, materializer, tempFileCreator) + lazy val parsers: PlayBodyParsers = playBodyParsers - lazy val actionBuilder: ActionBuilder[Request, AnyContent] = DefaultActionBuilder(parsers.anyContent) + lazy val actionBuilder: ActionBuilder[Request, AnyContent] = defaultActionBuilder override lazy val injector = { new SimpleInjector(NewInstanceInjector) + From f951385bb8d06cce54c41f87c9b93be4bdb5f002 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Tue, 21 Feb 2017 15:25:55 -0800 Subject: [PATCH 16/66] Update MyApplicationLoader.scala --- .travis.yml | 1 - app/MyApplicationLoader.scala | 20 -------------------- test/MyApplicationFactory.scala | 25 ++++++++++++++++++++++++- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/.travis.yml b/.travis.yml index b63e07068..ec97334dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: scala scala: -- 2.11.8 - 2.12.1 jdk: - oraclejdk8 diff --git a/app/MyApplicationLoader.scala b/app/MyApplicationLoader.scala index 0c5aad646..4794e46f2 100644 --- a/app/MyApplicationLoader.scala +++ b/app/MyApplicationLoader.scala @@ -3,7 +3,6 @@ import play.api.inject._ import play.api.libs.ws.ahc._ import play.api.mvc._ import play.api.routing.Router -import play.core.DefaultWebCommands class MyApplicationLoader extends ApplicationLoader { def load(context: ApplicationLoader.Context) = { @@ -14,25 +13,6 @@ class MyApplicationLoader extends ApplicationLoader { } } -/** - * An application builder for running an application in tests - */ -class MyApplicationBuilder { - - def build(): Application = { - val env = Environment.simple() - val context = ApplicationLoader.Context( - environment = env, - sourceMapper = None, - webCommands = new DefaultWebCommands(), - initialConfiguration = Configuration.load(env), - lifecycle = new DefaultApplicationLifecycle() - ) - val loader = new MyApplicationLoader() - loader.load(context) - } -} - class MyComponents(context: ApplicationLoader.Context) extends BuiltInComponentsFromContext(context) with AhcWSComponents diff --git a/test/MyApplicationFactory.scala b/test/MyApplicationFactory.scala index 3e57cefcb..e1831b780 100644 --- a/test/MyApplicationFactory.scala +++ b/test/MyApplicationFactory.scala @@ -1,7 +1,30 @@ import org.scalatestplus.play.FakeApplicationFactory -import play.api.Application +import play.api._ +import play.api.i18n._ +import play.api.inject._ +import play.core.DefaultWebCommands trait MyApplicationFactory extends FakeApplicationFactory { + + /** + * An application builder for running an application in tests + */ + class MyApplicationBuilder { + + def build(): Application = { + val env = Environment.simple() + val context = ApplicationLoader.Context( + environment = env, + sourceMapper = None, + webCommands = new DefaultWebCommands(), + initialConfiguration = Configuration.load(env), + lifecycle = new DefaultApplicationLifecycle() + ) + val loader = new MyApplicationLoader() + loader.load(context) + } + } + override def fakeApplication(): Application = { new MyApplicationBuilder().build() } From c2ea7cd5b9a7e215b92eb51aef799ce78e93db0a Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Wed, 22 Feb 2017 17:04:06 -0800 Subject: [PATCH 17/66] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 620aac8a1..41c4166bb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Play Framework with Compile Time DI Tests -This is an example of Play 2.5.x using the Scala API with manually wired compile time dependency injection. +This is an example of Play using the Scala API with manually wired compile time dependency injection. The application loader here is `MyApplicationLoader` which uses `MyComponents` to wire together an injector. In testing, there are some places where a `WSClient` has to be used, and so some additional components have to be added in. From 8115a43e7265bc63493075c3132b02bfd4e385f8 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Thu, 2 Mar 2017 14:32:41 -0800 Subject: [PATCH 18/66] Use components directly --- app/MyApplicationLoader.scala | 28 +++++++++++----------------- build.sbt | 4 +--- test/ApplicationSpec.scala | 4 +++- test/MyApplicationFactory.scala | 33 +++++++++++++-------------------- test/ServerSpec.scala | 3 --- 5 files changed, 28 insertions(+), 44 deletions(-) diff --git a/app/MyApplicationLoader.scala b/app/MyApplicationLoader.scala index 4794e46f2..32bf1e92d 100644 --- a/app/MyApplicationLoader.scala +++ b/app/MyApplicationLoader.scala @@ -1,41 +1,35 @@ import play.api._ -import play.api.inject._ import play.api.libs.ws.ahc._ import play.api.mvc._ import play.api.routing.Router class MyApplicationLoader extends ApplicationLoader { - def load(context: ApplicationLoader.Context) = { + var components: MyComponents = _ + + def load(context: ApplicationLoader.Context): Application = { LoggerConfigurator(context.environment.classLoader).foreach { _.configure(context.environment) } - new MyComponents(context).application + + components = new MyComponents(context) + components.application } } class MyComponents(context: ApplicationLoader.Context) extends BuiltInComponentsFromContext(context) with AhcWSComponents - with _root_.controllers.AssetsComponents - with ControllerComponents { + with _root_.controllers.AssetsComponents { lazy val parsers: PlayBodyParsers = playBodyParsers lazy val actionBuilder: ActionBuilder[Request, AnyContent] = defaultActionBuilder - override lazy val injector = { - new SimpleInjector(NewInstanceInjector) + - router + - cookieSigner + - csrfTokenSigner + - httpConfiguration + - tempFileCreator + - wsClient + - messagesApi - } + lazy val controllerComponents: ControllerComponents = DefaultControllerComponents( + defaultActionBuilder, playBodyParsers, messagesApi, langs, fileMimeTypes, executionContext + ) - lazy val homeController = new _root_.controllers.HomeController(this) + lazy val homeController = new _root_.controllers.HomeController(controllerComponents) lazy val router: Router = new _root_.router.Routes(httpErrorHandler, homeController, assets) - } diff --git a/build.sbt b/build.sbt index 96ce6a769..c85f50439 100644 --- a/build.sbt +++ b/build.sbt @@ -1,4 +1,4 @@ -name := """play-scala-compile-di-with-tests""" +name := """play-scala-compile-di-example""" version := "1.0-SNAPSHOT" @@ -9,5 +9,3 @@ scalaVersion := "2.12.1" libraryDependencies += guice libraryDependencies += ws libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "2.0.0-M2" % Test - -resolvers += "scalaz-bintray" at "http://dl.bintray.com/scalaz/releases" diff --git a/test/ApplicationSpec.scala b/test/ApplicationSpec.scala index 1cb78001c..fc38b4f17 100644 --- a/test/ApplicationSpec.scala +++ b/test/ApplicationSpec.scala @@ -1,3 +1,4 @@ +import org.scalatest.concurrent.ScalaFutures import org.scalatestplus.play._ import play.api.test.Helpers._ import play.api.test._ @@ -7,7 +8,8 @@ import play.api.test._ */ class ApplicationSpec extends PlaySpec with BaseOneAppPerTest - with MyApplicationFactory { + with MyApplicationFactory + with ScalaFutures { "Routes" should { "send 404 on a bad request" in { diff --git a/test/MyApplicationFactory.scala b/test/MyApplicationFactory.scala index e1831b780..4dcaf11f3 100644 --- a/test/MyApplicationFactory.scala +++ b/test/MyApplicationFactory.scala @@ -1,31 +1,24 @@ import org.scalatestplus.play.FakeApplicationFactory import play.api._ -import play.api.i18n._ import play.api.inject._ +import play.api.libs.ws.WSClient import play.core.DefaultWebCommands trait MyApplicationFactory extends FakeApplicationFactory { - /** - * An application builder for running an application in tests - */ - class MyApplicationBuilder { + private val loader = new MyApplicationLoader() - def build(): Application = { - val env = Environment.simple() - val context = ApplicationLoader.Context( - environment = env, - sourceMapper = None, - webCommands = new DefaultWebCommands(), - initialConfiguration = Configuration.load(env), - lifecycle = new DefaultApplicationLifecycle() - ) - val loader = new MyApplicationLoader() - loader.load(context) - } + override def fakeApplication: Application = { + val env = Environment.simple() + val context = ApplicationLoader.Context( + environment = env, + sourceMapper = None, + webCommands = new DefaultWebCommands(), + initialConfiguration = Configuration.load(env), + lifecycle = new DefaultApplicationLifecycle() + ) + loader.load(context) } - override def fakeApplication(): Application = { - new MyApplicationBuilder().build() - } + implicit def wsClient: WSClient = loader.components.wsClient } diff --git a/test/ServerSpec.scala b/test/ServerSpec.scala index e7b47fb4b..439187f08 100644 --- a/test/ServerSpec.scala +++ b/test/ServerSpec.scala @@ -10,11 +10,8 @@ import play.api.test.Injecting class ServerSpec extends PlaySpec with BaseOneServerPerSuite with MyApplicationFactory - with Injecting with ScalaFutures { - implicit val wsClient: WSClient = inject[WSClient] - "Server query should" should { "work" in { whenReady(wsUrl("/").get) { response => From 94bd52d0a18b7002a00a3cc1b6fb883e6343169a Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Thu, 2 Mar 2017 14:41:25 -0800 Subject: [PATCH 19/66] Use wsclient explicitly --- test/ServerSpec.scala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/ServerSpec.scala b/test/ServerSpec.scala index 439187f08..286e7de2f 100644 --- a/test/ServerSpec.scala +++ b/test/ServerSpec.scala @@ -14,12 +14,10 @@ class ServerSpec extends PlaySpec "Server query should" should { "work" in { - whenReady(wsUrl("/").get) { response => + whenReady(wsClient.url(s"http://localhost:${port}/").get) { response => response.status mustBe play.api.http.Status.OK } } - } - } From 8d234e8bebb999fe932a6195547006d8d8f74206 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Thu, 2 Mar 2017 15:21:49 -0800 Subject: [PATCH 20/66] Add secret key --- conf/application.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/conf/application.conf b/conf/application.conf index 50f8f9d3e..7988436c4 100644 --- a/conf/application.conf +++ b/conf/application.conf @@ -1,2 +1,3 @@ +play.http.secret.key="rosebud" play.application.loader=MyApplicationLoader From 831ad64056f6288869a016c91b4054e6164722e0 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Tue, 28 Mar 2017 13:57:27 -0700 Subject: [PATCH 21/66] Updated with template-control on 2017-03-28T20:57:27.932Z /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 96ce6a769..a002f1754 100644 --- a/build.sbt +++ b/build.sbt @@ -8,6 +8,6 @@ scalaVersion := "2.12.1" libraryDependencies += guice libraryDependencies += ws -libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "2.0.0-M2" % Test +libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "3.0.0-M2" % Test resolvers += "scalaz-bintray" at "http://dl.bintray.com/scalaz/releases" diff --git a/project/plugins.sbt b/project/plugins.sbt index 8292bc8ad..77b35a40a 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,2 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-M1") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-M3") From ec7896d454395e3b21e5e4c3a5b87d0359a0a069 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Sun, 2 Apr 2017 14:21:14 -0700 Subject: [PATCH 22/66] Upgrade spec --- .travis.yml | 1 + app/MyApplicationLoader.scala | 12 +----------- app/views/index.scala.html | 8 +------- build.sbt | 2 +- test/ServerSpec.scala | 10 +++++----- 5 files changed, 9 insertions(+), 24 deletions(-) diff --git a/.travis.yml b/.travis.yml index ec97334dc..b63e07068 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ language: scala scala: +- 2.11.8 - 2.12.1 jdk: - oraclejdk8 diff --git a/app/MyApplicationLoader.scala b/app/MyApplicationLoader.scala index 4794e46f2..92f0acf64 100644 --- a/app/MyApplicationLoader.scala +++ b/app/MyApplicationLoader.scala @@ -16,6 +16,7 @@ class MyApplicationLoader extends ApplicationLoader { class MyComponents(context: ApplicationLoader.Context) extends BuiltInComponentsFromContext(context) with AhcWSComponents + with play.filters.HttpFiltersComponents with _root_.controllers.AssetsComponents with ControllerComponents { @@ -23,17 +24,6 @@ class MyComponents(context: ApplicationLoader.Context) lazy val actionBuilder: ActionBuilder[Request, AnyContent] = defaultActionBuilder - override lazy val injector = { - new SimpleInjector(NewInstanceInjector) + - router + - cookieSigner + - csrfTokenSigner + - httpConfiguration + - tempFileCreator + - wsClient + - messagesApi - } - lazy val homeController = new _root_.controllers.HomeController(this) lazy val router: Router = new _root_.router.Routes(httpErrorHandler, homeController, assets) diff --git a/app/views/index.scala.html b/app/views/index.scala.html index da59cbfe0..4488bad13 100644 --- a/app/views/index.scala.html +++ b/app/views/index.scala.html @@ -10,11 +10,5 @@ * argument is an `Html` object containing the body of the page. *@ @main("Welcome to Play") { - - @* - * Get an `Html` object by calling the built-in Play welcome - * template and passing a `String` message. - *@ - @play20.welcome(message, style = "Scala") - +

Your message is: @message

} diff --git a/build.sbt b/build.sbt index a002f1754..fc8062569 100644 --- a/build.sbt +++ b/build.sbt @@ -1,4 +1,4 @@ -name := """play-scala-compile-di-with-tests""" +name := """play-scala-compile-di-example""" version := "1.0-SNAPSHOT" diff --git a/test/ServerSpec.scala b/test/ServerSpec.scala index e7b47fb4b..938cd1d73 100644 --- a/test/ServerSpec.scala +++ b/test/ServerSpec.scala @@ -1,8 +1,7 @@ import org.scalatest.concurrent.ScalaFutures import org.scalatestplus.play._ import play.api.libs.ws.WSClient -import play.api.mvc.Results -import play.api.test.Injecting +import play.api.test._ /** * Runs a play server on the default test port (Helpers.testServerPort == 19001). @@ -10,14 +9,15 @@ import play.api.test.Injecting class ServerSpec extends PlaySpec with BaseOneServerPerSuite with MyApplicationFactory - with Injecting with ScalaFutures { - implicit val wsClient: WSClient = inject[WSClient] + implicit val intPort = Helpers.testServerPort + + val wsClient = WsTestClient "Server query should" should { "work" in { - whenReady(wsUrl("/").get) { response => + whenReady(wsClient.wsUrl("/").get) { response => response.status mustBe play.api.http.Status.OK } } From a311e6f8dddbaa3a630362d6f0a786d47cbef3d6 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Sun, 2 Apr 2017 21:04:37 -0700 Subject: [PATCH 23/66] add travis --- .travis.yml | 5 +++-- test/ServerSpec.scala | 13 +++++-------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index b63e07068..dc4f5acfc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,7 @@ language: scala +dist: trusty +sudo: true +group: beta scala: - 2.11.8 - 2.12.1 @@ -8,8 +11,6 @@ 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: diff --git a/test/ServerSpec.scala b/test/ServerSpec.scala index 938cd1d73..cf447cfd6 100644 --- a/test/ServerSpec.scala +++ b/test/ServerSpec.scala @@ -1,7 +1,5 @@ -import org.scalatest.concurrent.ScalaFutures +import org.scalatest.concurrent.{IntegrationPatience, ScalaFutures} import org.scalatestplus.play._ -import play.api.libs.ws.WSClient -import play.api.test._ /** * Runs a play server on the default test port (Helpers.testServerPort == 19001). @@ -9,15 +7,14 @@ import play.api.test._ class ServerSpec extends PlaySpec with BaseOneServerPerSuite with MyApplicationFactory - with ScalaFutures { + with ScalaFutures + with IntegrationPatience { - implicit val intPort = Helpers.testServerPort - - val wsClient = WsTestClient + private implicit val implicitPort = port "Server query should" should { "work" in { - whenReady(wsClient.wsUrl("/").get) { response => + whenReady(play.api.test.WsTestClient.wsUrl("/").get) { response => response.status mustBe play.api.http.Status.OK } } From 2f7835e86bc644058e2934b3e8704f43ba786bce Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Sat, 8 Apr 2017 21:09:18 -0700 Subject: [PATCH 24/66] Updated with template-control on 2017-04-09T04:09:18.121Z /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 77b35a40a..871e5bd11 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,2 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-M3") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-M4") From c1aa4012b122bc9239cf7438e83424590359e05e Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Mon, 10 Apr 2017 21:20:40 -0700 Subject: [PATCH 25/66] Updated with template-control on 2017-04-11T04:20:40.312Z /LICENSE: wrote /LICENSE **/build.properties: sbt.version=0.13.15 --- project/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 7adc6ac2263c2d47aabce5a3aeb9ffddc2642a53 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Tue, 18 Apr 2017 12:22:44 -0700 Subject: [PATCH 26/66] Updated with template-control on 2017-04-18T19:22:44.014Z /LICENSE: wrote /LICENSE **/build.sbt: scalaVersion := "2.12.2" --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 5059fdb05..d29c08853 100644 --- a/build.sbt +++ b/build.sbt @@ -4,7 +4,7 @@ version := "1.0-SNAPSHOT" lazy val root = (project in file(".")).enablePlugins(PlayScala) -scalaVersion := "2.12.1" +scalaVersion := "2.12.2" libraryDependencies += guice libraryDependencies += ws From 68dd429d20071ff07691d9340342aa4fe928c158 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Tue, 18 Apr 2017 13:11:50 -0700 Subject: [PATCH 27/66] Updated with template-control on 2017-04-18T20:11:50.413Z /LICENSE: wrote /LICENSE **/build.sbt: libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "3.0.0-M3" % Test --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index d29c08853..551dfa32e 100644 --- a/build.sbt +++ b/build.sbt @@ -8,4 +8,4 @@ scalaVersion := "2.12.2" libraryDependencies += guice libraryDependencies += ws -libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "3.0.0-M2" % Test +libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "3.0.0-M3" % Test From 7e944c25170e3f3977ec685b3392a7cf2f9c12cd Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Thu, 27 Apr 2017 18:46:57 -0700 Subject: [PATCH 28/66] Updated with template-control on 2017-04-28T01:46:57.741Z /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 871e5bd11..e2831ab29 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,2 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-M4") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-M5") From d97373bab7722e99fc1a723c2031ae3bb353c596 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Fri, 28 Apr 2017 19:15:15 -0700 Subject: [PATCH 29/66] Update for 2.6.0-M5 --- app/MyApplicationLoader.scala | 13 +------------ build.sbt | 1 - conf/application.conf | 2 -- test/MyApplicationFactory.scala | 12 ++++++------ 4 files changed, 7 insertions(+), 21 deletions(-) diff --git a/app/MyApplicationLoader.scala b/app/MyApplicationLoader.scala index 1ae98f8aa..189993818 100644 --- a/app/MyApplicationLoader.scala +++ b/app/MyApplicationLoader.scala @@ -1,16 +1,10 @@ import play.api._ -import play.api.libs.ws.ahc._ -import play.api.mvc._ import play.api.routing.Router class MyApplicationLoader extends ApplicationLoader { - var components: MyComponents = _ + private var components: MyComponents = _ def load(context: ApplicationLoader.Context): Application = { - LoggerConfigurator(context.environment.classLoader).foreach { - _.configure(context.environment) - } - components = new MyComponents(context) components.application } @@ -18,14 +12,9 @@ class MyApplicationLoader extends ApplicationLoader { class MyComponents(context: ApplicationLoader.Context) extends BuiltInComponentsFromContext(context) - with AhcWSComponents with play.filters.HttpFiltersComponents with _root_.controllers.AssetsComponents { - lazy val parsers: PlayBodyParsers = playBodyParsers - - lazy val actionBuilder: ActionBuilder[Request, AnyContent] = defaultActionBuilder - lazy val homeController = new _root_.controllers.HomeController(controllerComponents) lazy val router: Router = new _root_.router.Routes(httpErrorHandler, homeController, assets) diff --git a/build.sbt b/build.sbt index 551dfa32e..3941647e1 100644 --- a/build.sbt +++ b/build.sbt @@ -7,5 +7,4 @@ lazy val root = (project in file(".")).enablePlugins(PlayScala) scalaVersion := "2.12.2" libraryDependencies += guice -libraryDependencies += ws libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "3.0.0-M3" % Test diff --git a/conf/application.conf b/conf/application.conf index 7988436c4..eac8bafef 100644 --- a/conf/application.conf +++ b/conf/application.conf @@ -1,3 +1 @@ -play.http.secret.key="rosebud" - play.application.loader=MyApplicationLoader diff --git a/test/MyApplicationFactory.scala b/test/MyApplicationFactory.scala index 4dcaf11f3..70bd1d85f 100644 --- a/test/MyApplicationFactory.scala +++ b/test/MyApplicationFactory.scala @@ -1,24 +1,24 @@ +import java.io.File + import org.scalatestplus.play.FakeApplicationFactory import play.api._ import play.api.inject._ -import play.api.libs.ws.WSClient import play.core.DefaultWebCommands trait MyApplicationFactory extends FakeApplicationFactory { - private val loader = new MyApplicationLoader() - override def fakeApplication: Application = { - val env = Environment.simple() + val env = Environment.simple(new File(".")) + val configuration = Configuration.load(env) val context = ApplicationLoader.Context( environment = env, sourceMapper = None, webCommands = new DefaultWebCommands(), - initialConfiguration = Configuration.load(env), + initialConfiguration = configuration, lifecycle = new DefaultApplicationLifecycle() ) + val loader = new MyApplicationLoader() loader.load(context) } - implicit def wsClient: WSClient = loader.components.wsClient } From 6e882b2fbeca7d995b605b0a33a475072fdeb1e8 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Fri, 28 Apr 2017 20:07:20 -0700 Subject: [PATCH 30/66] Better README --- .travis.yml | 4 +- README.md | 141 +++++++++++++--------------------------------------- 2 files changed, 36 insertions(+), 109 deletions(-) diff --git a/.travis.yml b/.travis.yml index dc4f5acfc..72861bfe6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,8 @@ dist: trusty sudo: true group: beta scala: -- 2.11.8 -- 2.12.1 +- 2.11.11 +- 2.12.2 jdk: - oraclejdk8 cache: diff --git a/README.md b/README.md index 41c4166bb..146db917e 100644 --- a/README.md +++ b/README.md @@ -1,131 +1,58 @@ -# Play Framework with Compile Time DI Tests +[](https://travis-ci.org/playframework/play-scala-compile-di-example) -This is an example of Play using the Scala API with manually wired compile time dependency injection. - -The application loader here is `MyApplicationLoader` which uses `MyComponents` to wire together an injector. In testing, there are some places where a `WSClient` has to be used, and so some additional components have to be added in. - -To do this, the injector has to be implemented specifying all of the built in components, plus the WS API, which is made available through `AhcWSComponents`: - -``` scala -import play.api._ -import play.api.i18n._ -import play.api.inject._ -import play.api.libs.ws.ahc.AhcWSComponents -import play.api.routing.Router -import router.Routes - -class MyApplicationLoader extends ApplicationLoader { - def load(context: ApplicationLoader.Context) = { - LoggerConfigurator(context.environment.classLoader).foreach { - _.configure(context.environment) - } - new MyComponents(context).application - } -} - -class MyComponents(context: ApplicationLoader.Context) - extends BuiltInComponentsFromContext(context) - with I18nComponents - with AhcWSComponents { - - override lazy val injector = { - new SimpleInjector(NewInstanceInjector) + - router + - cookieSigner + - csrfTokenSigner + - httpConfiguration + - tempFileCreator + - global + - crypto + - wsApi + - messagesApi - } - - lazy val router: Router = new Routes(httpErrorHandler, homeController, assets) - - lazy val homeController = new controllers.HomeController() - lazy val assets = new controllers.Assets(httpErrorHandler) -} -``` - -Now that `MyComponents` has the `AhcWSComponents` trait, it can use `components.wsClient` anywhere. - -The ScalaTest suite mixins such as `OneAppPerSuite` use `GuiceApplicationLoader` for all the implicit Application set up, so to use dependency injection, the trait must be extended to use the components using types: - -``` scala -trait OneServerPerSuiteWithComponents[T <: BuiltInComponents] - extends OneServerPerSuite - with WithApplicationComponents[T] { - this: Suite => +# play-scala-compile-di-example - override implicit lazy val app: Application = newApplication -} -``` - -where `WithApplicationComponents` is defined as: - -``` scala -trait WithApplicationComponents[T <: BuiltInComponents] { - private var _components: T = _ - - // accessed to get the components in tests - final def components: T = _components - - // overridden by subclasses - def createComponents(context: Context): T +This is an example of Play using the Scala API with manually wired compile time dependency injection. - // creates a new application and sets the components - def newApplication: Application = { - _components = createComponents(context) - _components.application +The application loader here is `MyApplicationLoader` which uses `MyComponents` to wire together an injector. + +For testing, a `MyApplicationFactory` is defined and mixed in: + +```scala +trait MyApplicationFactory extends FakeApplicationFactory { + + override def fakeApplication: Application = { + val env = Environment.simple(new File(".")) + val configuration = Configuration.load(env) + val context = ApplicationLoader.Context( + environment = env, + sourceMapper = None, + webCommands = new DefaultWebCommands(), + initialConfiguration = configuration, + lifecycle = new DefaultApplicationLifecycle() + ) + val loader = new MyApplicationLoader() + loader.load(context) } - def context: ApplicationLoader.Context = { - val classLoader = ApplicationLoader.getClass.getClassLoader - val env = new Environment(new java.io.File("."), classLoader, Mode.Test) - ApplicationLoader.createContext(env) - } } ``` -Then, depending on your components, you can set up a subtype of `OneServerPerSuiteWithComponents` using `MyComponents`: - -``` scala -trait OneServerPerSuiteWithMyComponents - extends OneServerPerSuiteWithComponents[MyComponents] { - this: Suite => +Once the `MyApplicationFactory` is defined, the fake application is used by TestSuite types: - override def createComponents(context: Context): MyComponents = new MyComponents(context) -} -``` - -Once the `OneServerPerSuiteWithMyComponents` is defined, you must call out to the `app` lazy val, after which you can access the `components` field to get at the wsClient: - -``` scala +```scala class ServerSpec extends PlaySpec - with OneServerPerSuiteWithMyComponents - with Results - with ScalaFutures { + with BaseOneServerPerSuite + with MyApplicationFactory + with ScalaFutures + with IntegrationPatience { - "Server query should" should { + private implicit val implicitPort = port + "Server query should" should { "work" in { - implicit val ec = app.materializer.executionContext - val wsClient = components.wsClient - - whenReady(wsUrl("/")(portNumber, wsClient).get) { response => - response.status mustBe OK + whenReady(play.api.test.WsTestClient.wsUrl("/").get) { response => + response.status mustBe play.api.http.Status.OK } } - } } ``` ## Further Documentation -* https://www.playframework.com/documentation/2.5.x/ScalaCompileTimeDependencyInjection -* https://www.playframework.com/documentation/2.5.x/ScalaTestingWithScalaTest#Using-ScalaTest-+-Play +* https://www.playframework.com/documentation/latest/ScalaCompileTimeDependencyInjection +* https://www.playframework.com/documentation/latest/ScalaTestingWithScalaTest#Using-ScalaTest-+-Play * http://www.scalatest.org/user_guide * http://www.scalatest.org/release_notes/3.0.0 * https://github.com/playframework/scalatestplus-play From d499257702558d47064075299c17d92b9d60b1aa Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Mon, 1 May 2017 12:43:31 -0700 Subject: [PATCH 31/66] Remove guice --- build.sbt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index 3941647e1..93ffe21cb 100644 --- a/build.sbt +++ b/build.sbt @@ -5,6 +5,5 @@ version := "1.0-SNAPSHOT" lazy val root = (project in file(".")).enablePlugins(PlayScala) scalaVersion := "2.12.2" - -libraryDependencies += guice +s libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "3.0.0-M3" % Test From 9e9bea60d9931c7cfeeefd5db6afa3ede74659a1 Mon Sep 17 00:00:00 2001 From: Will Sargent Date: Mon, 1 May 2017 12:44:45 -0700 Subject: [PATCH 32/66] Remove guice --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 93ffe21cb..dd7ba9e67 100644 --- a/build.sbt +++ b/build.sbt @@ -5,5 +5,5 @@ version := "1.0-SNAPSHOT" lazy val root = (project in file(".")).enablePlugins(PlayScala) scalaVersion := "2.12.2" -s + libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "3.0.0-M3" % Test From 6dfd47234d77ec17f3d60465a8686206d78c1a8a Mon Sep 17 00:00:00 2001 From: Greg Methvin Date: Sun, 4 Jun 2017 00:10:54 +0200 Subject: [PATCH 33/66] Updated with template-control on 2017-06-03T16:01:09.192Z (#40) **/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 e2831ab29..4955cd70e 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,2 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-M5") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-RC2") From 75856f2077494367c990273b93e9956f16bfd140 Mon Sep 17 00:00:00 2001 From: Greg Methvin Date: Fri, 23 Jun 2017 02:18:35 +0000 Subject: [PATCH 34/66] Updated with template-control on 2017-06-23T02:18:35.741Z **/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 4955cd70e..90330f7ef 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,2 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-RC2") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0") From 7b42d9727b239663ca715c6fca38db81a773a17a Mon Sep 17 00:00:00 2001 From: Greg Methvin Date: Fri, 7 Jul 2017 00:45:45 +0000 Subject: [PATCH 35/66] Updated with template-control on 2017-07-07T00:45:45.190Z **/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 90330f7ef..ac1a34d4a 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,2 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.1") From fd1282d1a7f74e0d44f249d9bfe5d3d03790d144 Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Thu, 20 Jul 2017 00:06:20 -0300 Subject: [PATCH 36/66] Updated with template-control on 2017-07-20T01:10:18.951Z (#45) **/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 ac1a34d4a..2052d7ca4 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,2 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.1") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.2") From aa054a9f692483dfcd87e75362029bb796218e24 Mon Sep 17 00:00:00 2001 From: Rich Dougherty Date: Sat, 12 Aug 2017 16:31:21 +1200 Subject: [PATCH 37/66] Updated with template-control on 2017-08-12T02:43:20.525Z (#46) **/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 2052d7ca4..66fbf368a 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,2 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.2") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.3") From 440c462394f0d7145e23c8e04148cfe8cc5c3ea6 Mon Sep 17 00:00:00 2001 From: Greg Methvin Date: Thu, 14 Sep 2017 23:29:36 +0000 Subject: [PATCH 38/66] Updated with template-control on 2017-09-14T23:29:36.682Z **/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 66fbf368a..704e7f892 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,2 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.3") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.5") From cb67df274ff3762287f373e5baab7b7085308609 Mon Sep 17 00:00:00 2001 From: Greg Methvin Date: Fri, 6 Oct 2017 11:33:04 -0700 Subject: [PATCH 39/66] Updated with template-control on 2017-10-05T23:18:58.939Z (#49) **/build.properties: sbt.version=1.0.2 **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.6") --- 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 64317fdae..b7dd3cb2a 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=0.13.15 +sbt.version=1.0.2 diff --git a/project/plugins.sbt b/project/plugins.sbt index 704e7f892..cc959dd08 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,2 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.5") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.6") From 2aea3880ac737e1c2a1a7741979aa22a224a56a4 Mon Sep 17 00:00:00 2001 From: Greg Methvin Date: Thu, 2 Nov 2017 02:51:11 +0000 Subject: [PATCH 40/66] Updated with template-control on 2017-11-02T02:51:11.548Z **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.7") --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index cc959dd08..6bc77e207 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,2 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.6") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.7") From ee78735e606d557dbd3ff4030dc1d7bc42452a5c Mon Sep 17 00:00:00 2001 From: Greg Methvin Date: Sat, 9 Dec 2017 03:16:27 +0000 Subject: [PATCH 41/66] Updated with template-control on 2017-12-09T03:16:27.598Z **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.9") --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 6bc77e207..51cd14953 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,2 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.7") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.9") From 7a343496f17d286cfd994e20f0d06b665388c2f3 Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Tue, 19 Dec 2017 17:11:09 -0200 Subject: [PATCH 42/66] Add Gradle configuration and Java 9 support (#54) --- .gitignore | 3 + .travis.yml | 29 +++- README.md | 16 +-- build.gradle | 41 ++++++ build.sbt | 4 +- gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54329 bytes gradle/wrapper/gradle-wrapper.properties | 5 + gradlew | 172 +++++++++++++++++++++++ gradlew.bat | 84 +++++++++++ project/build.properties | 2 +- scripts/script-helper | 13 ++ scripts/test-gradle | 13 ++ scripts/test-sbt | 8 ++ 13 files changed, 374 insertions(+), 16 deletions(-) create mode 100644 build.gradle create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 scripts/script-helper create mode 100755 scripts/test-gradle create mode 100755 scripts/test-sbt diff --git a/.gitignore b/.gitignore index 1db792587..eb0b5d686 100644 --- a/.gitignore +++ b/.gitignore @@ -37,9 +37,12 @@ server.pid .idea/uiDesigner.xml # Gradle: +build +.gradle .idea/gradle.xml .idea/libraries + # Mongo Explorer plugin: .idea/mongoSettings.xml diff --git a/.travis.yml b/.travis.yml index 72861bfe6..a5b65ce36 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,17 +1,36 @@ language: scala -dist: trusty -sudo: true -group: beta scala: -- 2.11.11 -- 2.12.2 +# When updating Scala versions, also check the excludes +# in build matrix below. +- 2.11.12 +- 2.12.4 jdk: - oraclejdk8 +- oraclejdk9 +env: + matrix: + - SCRIPT=scripts/test-sbt + - SCRIPT=scripts/test-gradle +script: +- $SCRIPT cache: directories: - "$HOME/.ivy2/cache" + - "$HOME/.gradle/caches" 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 + +# See https://blog.travis-ci.com/2014-03-13-slack-notifications/ +# created with travis encrypt command line tool notifications: slack: secure: e2qJtRrHD8Mb9GhZXXeVzfxwZjRgKBq1FUv825gXB+iRm1Vn2J+Ysk3Hv7xrANxo2A6JXkPMtZG8wyzcYDcKrRX7CxMYbytlrqI0+7v8VicdO8wDerplvROmWoYpKOFmxza0wz3ENL8IEo2pkD8baPWYty9a+GEIGwuyKsuqGNEKzW7JHtWZXdKE8gEQKneNxErtFclI4yQqHk0NHJu+8/+BViNQ1PzaRAIEOj690x2ls9x2joEIQzSUeMdNBbjc5Xi45dDPgjb5jYwwjLRqChSOrEDOdF6cPpSYFXENizVd85LaoES3iL+1J8lG7HBARCvtASn+5KiXwVUtPCoiabqE4jmTkhi124PLU17fnhJSboPn5G8FrjoCYPhzSOFzWAkwAIsGBKbL4pC+sqh/HFbh/k/YwyksBZIrWpu0BfgVpCxqaf/1fM5Yj12yth1QLrDz5dOSoeJN2jovKyD9UAJlhq+SOBKpYBE3g6TTcRPFqNxY0gSZPCXP25XTxExWX1ZV76zAjh8gJcTpBzpqyOAyuylrfRfSnS8TrM81xeh49XnJMqSDHsDBnjiWKyAp4xtVG1Rch4akNcfBPf7LKL4Ve2JM7rRWijw4cIRBX1VpZ4SRwafdNLi4xIZimtVnye+nk9K1DgwmItls78mACWOKIzGsPf+DI6NALmU2M/8= diff --git a/README.md b/README.md index 146db917e..219f948ac 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -[](https://travis-ci.org/playframework/play-scala-compile-di-example) - # play-scala-compile-di-example +[](https://travis-ci.org/playframework/play-scala-compile-di-example) + This is an example of Play using the Scala API with manually wired compile time dependency injection. The application loader here is `MyApplicationLoader` which uses `MyComponents` to wire together an injector. - + For testing, a `MyApplicationFactory` is defined and mixed in: ```scala @@ -51,8 +51,8 @@ class ServerSpec extends PlaySpec ## Further Documentation -* https://www.playframework.com/documentation/latest/ScalaCompileTimeDependencyInjection -* https://www.playframework.com/documentation/latest/ScalaTestingWithScalaTest#Using-ScalaTest-+-Play -* http://www.scalatest.org/user_guide -* http://www.scalatest.org/release_notes/3.0.0 -* https://github.com/playframework/scalatestplus-play +* +* +* +* +* diff --git a/build.gradle b/build.gradle new file mode 100644 index 000000000..b286f5cd9 --- /dev/null +++ b/build.gradle @@ -0,0 +1,41 @@ +plugins { + id 'play' + id 'idea' +} + +def playVersion = '2.6.9' +def scalaVersion = System.getProperty("scala.binary.version", /* default = */ "2.12") + +model { + components { + play { + platform play: playVersion, scala: scalaVersion, java: '1.8' + injectedRoutesGenerator = true + + sources { + twirlTemplates { + defaultImports = TwirlImports.SCALA + } + } + } + } +} + +dependencies { + play "com.typesafe.play:play-logback_$scalaVersion:$playVersion" + play "com.typesafe.play:filters-helpers_$scalaVersion:$playVersion" + playTest "org.scalatestplus.play:scalatestplus-play_$scalaVersion:3.1.2" +} + +repositories { + jcenter() + maven { + name "lightbend-maven-releases" + url "https://repo.lightbend.com/lightbend/maven-release" + } + ivy { + name "lightbend-ivy-release" + url "https://repo.lightbend.com/lightbend/ivy-releases" + layout "ivy" + } +} diff --git a/build.sbt b/build.sbt index dd7ba9e67..4f6f7b6c6 100644 --- a/build.sbt +++ b/build.sbt @@ -4,6 +4,6 @@ version := "1.0-SNAPSHOT" lazy val root = (project in file(".")).enablePlugins(PlayScala) -scalaVersion := "2.12.2" +scalaVersion := "2.12.4" -libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "3.0.0-M3" % Test +libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "3.1.2" % Test diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..01b8bf6b1f99cad9213fc495b33ad5bbab8efd20 GIT binary patch literal 54329 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

<5G2M1WZP5 zzqSuO91lJod(SBDDw<*sX(+F6Uq~YAeYV#2A;XQu_p=N5X+#cmu19Qk>QAnV=k!?wbk5I;tDWgFc}0NkvC*G=V+Yh1cyeJVq~9czZiDXe+S=VfL2g`LWo8om z$Y~FQc6MFjV-t1Y`^D9XMwY*U_re2R?&(O~68T&D4S{X`6JYU-pz=}ew-)V0AOUT1 zVOkHAB-8uBcRjLvz<9HS#a@X*Kc@|W)nyiSgi|u5$Md|P()%2(?olGg@ypoJwp6>m z*dnfjjWC>?_1p;%1brqZyDRR;8EntVA92EJ3ByOxj6a+bhPl z;a?m4rQAV1@QU^#M1HX)0+}A<7TCO`ZR_RzF}X9-M>cRLyN4C+lCk2)kT^3gN^`IT zNP~fAm(wyIoR+l^lQDA(e1Yv}&$I!n?&*p6?lZcQ+vGLLd~fM)qt}wsbf3r=tmVYe zl)ntf#E!P7wlakP9MXS7m0nsAmqxZ*)#j;M&0De`oNmFgi$ov#!`6^4)iQyxg5Iuj zjLAhzQ)r`^hf7`*1`Rh`X;LVBtDSz@0T?kkT1o!ijeyTGt5vc^Cd*tmNgiNo^EaWvaC8$e+nb_{W01j3%=1Y&92YacjCi>eNbwk%-gPQ@H-+4xskQ}f_c=jg^S-# zYFBDf)2?@5cy@^@FHK5$YdAK9cI;!?Jgd}25lOW%xbCJ>By3=HiK@1EM+I46A)Lsd zeT|ZH;KlCml=@;5+hfYf>QNOr^XNH%J-lvev)$Omy8MZ`!{`j>(J5cG&ZXXgv)TaF zg;cz99i$4CX_@3MIb?GL0s*8J=3`#P(jXF(_(6DXZjc@(@h&=M&JG)9&Te1?(^XMW zjjC_70|b=9hB6pKQi`S^Ls7JyJw^@P>Ko^&q8F&?>6i;#CbxUiLz1ZH4lNyd@QACd zu>{!sqjB!2Dg}pbAXD>d!3jW}=5aN0b;rw*W>*PAxm7D)aw(c*RX2@bTGEI|RRp}vw7;NR2wa;rXN{L{Q#=Fa z$x@ms6pqb>!8AuV(prv>|aU8oWV={C&$c zMa=p=CDNOC2tISZcd8~18GN5oTbKY+Vrq;3_obJlfSKRMk;Hdp1`y`&LNSOqeauR_ z^j*Ojl3Ohzb5-a49A8s|UnM*NM8tg}BJXdci5%h&;$afbmRpN0&~9rCnBA`#lG!p zc{(9Y?A0Y9yo?wSYn>iigf~KP$0*@bGZ>*YM4&D;@{<%Gg5^uUJGRrV4 z(aZOGB&{_0f*O=Oi0k{@8vN^BU>s3jJRS&CJOl3o|BE{FAA&a#2YYiX3pZz@|Go-F z|Fly;7eX2OTs>R}<`4RwpHFs9nwh)B28*o5qK1Ge=_^w0m`uJOv!=&!tzt#Save(C zgKU=Bsgql|`ui(e1KVxR`?>Dx>(rD1$iWp&m`v)3A!j5(6vBm*z|aKm*T*)mo(W;R zNGo2`KM!^SS7+*9YxTm6YMm_oSrLceqN*nDOAtagULuZl5Q<7mOnB@Hq&P|#9y{5B z!2x+2s<%Cv2Aa0+u{bjZXS);#IFPk(Ph-K7K?3i|4ro> zRbqJoiOEYo(Im^((r}U4b8nvo_>4<`)ut`24?ILnglT;Pd&U}$lV3U$F9#PD(O=yV zgNNA=GW|(E=&m_1;uaNmipQe?pon4{T=zK!N!2_CJL0E*R^XXIKf*wi!>@l}3_P9Z zF~JyMbW!+n-+>!u=A1ESxzkJy$DRuG+$oioG7(@Et|xVbJ#BCt;J43Nvj@MKvTxzy zMmjNuc#LXBxFAwIGZJk~^!q$*`FME}yKE8d1f5Mp}KHNq(@=Z8YxV}0@;YS~|SpGg$_jG7>_8WWYcVx#4SxpzlV9N4aO>K{c z$P?a_fyDzGX$Of3@ykvedGd<@-R;M^Shlj*SswJLD+j@hi_&_>6WZ}#AYLR0iWMK|A zH_NBeu(tMyG=6VO-=Pb>-Q#$F*or}KmEGg*-n?vWQREURdB#+6AvOj*I%!R-4E_2$ zU5n9m>RWs|Wr;h2DaO&mFBdDb-Z{APGQx$(L`if?C|njd*fC=rTS%{o69U|meRvu?N;Z|Y zbT|ojL>j;q*?xXmnHH#3R4O-59NV1j=uapkK7}6@Wo*^Nd#(;$iuGsb;H315xh3pl zHaJ>h-_$hdNl{+|Zb%DZH%ES;*P*v0#}g|vrKm9;j-9e1M4qX@zkl&5OiwnCz=tb6 zz<6HXD+rGIVpGtkb{Q^LIgExOm zz?I|oO9)!BOLW#krLmWvX5(k!h{i>ots*EhpvAE;06K|u_c~y{#b|UxQ*O@Ks=bca z^_F0a@61j3I(Ziv{xLb8AXQj3;R{f_l6a#H5ukg5rxwF9A$?Qp-Mo54`N-SKc}fWp z0T)-L@V$$&my;l#Ha{O@!fK4-FSA)L&3<${Hcwa7ue`=f&YsXY(NgeDU#sRlT3+9J z6;(^(sjSK@3?oMo$%L-nqy*E;3pb0nZLx6 z;h5)T$y8GXK1DS-F@bGun8|J(v-9o=42&nLJy#}M5D0T^5VWBNn$RpC zZzG6Bt66VY4_?W=PX$DMpKAI!d`INr) zkMB{XPQ<52rvWVQqgI0OL_NWxoe`xxw&X8yVftdODPj5|t}S6*VMqN$-h9)1MBe0N zYq?g0+e8fJCoAksr0af1)FYtz?Me!Cxn`gUx&|T;)695GG6HF7!Kg1zzRf_{VWv^bo81v4$?F6u2g|wxHc6eJQAg&V z#%0DnWm2Rmu71rPJ8#xFUNFC*V{+N_qqFH@gYRLZ6C?GAcVRi>^n3zQxORPG)$-B~ z%_oB?-%Zf7d*Fe;cf%tQwcGv2S?rD$Z&>QC2X^vwYjnr5pa5u#38cHCt4G3|efuci z@3z=#A13`+ztmp;%zjXwPY_aq-;isu*hecWWX_=Z8paSqq7;XYnUjK*T>c4~PR4W7 z#C*%_H&tfGx`Y$w7`dXvVhmovDnT>btmy~SLf>>~84jkoQ%cv=MMb+a{JV&t0+1`I z32g_Y@yDhKe|K^PevP~MiiVl{Ou7^Mt9{lOnXEQ`xY^6L8D$705GON{!1?1&YJEl#fTf5Z)da=yiEQ zGgtC-soFGOEBEB~ZF_{7b(76En>d}mI~XIwNw{e>=Fv)sgcw@qOsykWr?+qAOZSVrQfg}TNI ztKNG)1SRrAt6#Q?(me%)>&A_^DM`pL>J{2xu>xa$3d@90xR61TQDl@fu%_85DuUUA za9tn64?At;{`BAW6oykwntxHeDpXsV#{tmt5RqdN7LtcF4vR~_kZNT|wqyR#z^Xcd zFdymVRZvyLfTpBT>w9<)Ozv@;Yk@dOSVWbbtm^y@@C>?flP^EgQPAwsy75bveo=}T zFxl(f)s)j(0#N_>Or(xEuV(n$M+`#;Pc$1@OjXEJZumkaekVqgP_i}p`oTx;terTx zZpT+0dpUya2hqlf`SpXN{}>PfhajNk_J0`H|2<5E;U5Vh4F8er z;RxLSFgpGhkU>W?IwdW~NZTyOBrQ84H7_?gviIf71l`EETodG9a1!8e{jW?DpwjL? zGEM&eCzwoZt^P*8KHZ$B<%{I}>46IT%jJ3AnnB5P%D2E2Z_ z1M!vr#8r}1|KTqWA4%67ZdbMW2YJ81b(KF&SQ2L1Qn(y-=J${p?xLMx3W7*MK;LFQ z6Z`aU;;mTL4XrrE;HY*Rkh6N%?qviUGNAKiCB~!P}Z->IpO6E(gGd7I#eDuT7j|?nZ zK}I(EJ>$Kb&@338M~O+em9(L!+=0zBR;JAQesx|3?Ok90)D1aS9P?yTh6Poh8Cr4X zk3zc=f2rE7jj+aP7nUsr@~?^EGP>Q>h#NHS?F{Cn`g-gD<8F&dqOh-0sa%pfL`b+1 zUsF*4a~)KGb4te&K0}bE>z3yb8% zibb5Q%Sfiv7feb1r0tfmiMv z@^4XYwg@KZI=;`wC)`1jUA9Kv{HKe2t$WmRcR4y8)VAFjRi zaz&O7Y2tDmc5+SX(bj6yGHYk$dBkWc96u3u&F)2yEE~*i0F%t9Kg^L6MJSb&?wrXi zGSc;_rln$!^ybwYBeacEFRsVGq-&4uC{F)*Y;<0y7~USXswMo>j4?~5%Zm!m@i@-> zXzi82sa-vpU{6MFRktJy+E0j#w`f`>Lbog{zP|9~hg(r{RCa!uGe>Yl536cn$;ouH za#@8XMvS-kddc1`!1LVq;h57~zV`7IYR}pp3u!JtE6Q67 zq3H9ZUcWPm2V4IukS}MCHSdF0qg2@~ufNx9+VMjQP&exiG_u9TZAeAEj*jw($G)zL zq9%#v{wVyOAC4A~AF=dPX|M}MZV)s(qI9@aIK?Pe+~ch|>QYb+78lDF*Nxz2-vpRbtQ*F4$0fDbvNM#CCatgQ@z1+EZWrt z2dZfywXkiW=no5jus-92>gXn5rFQ-COvKyegmL=4+NPzw6o@a?wGE-1Bt;pCHe;34K%Z z-FnOb%!nH;)gX+!a3nCk?5(f1HaWZBMmmC@lc({dUah+E;NOros{?ui1zPC-Q0);w zEbJmdE$oU$AVGQPdm{?xxI_0CKNG$LbY*i?YRQ$(&;NiA#h@DCxC(U@AJ$Yt}}^xt-EC_ z4!;QlLkjvSOhdx!bR~W|Ezmuf6A#@T`2tsjkr>TvW*lFCMY>Na_v8+{Y|=MCu1P8y z89vPiH5+CKcG-5lzk0oY>~aJC_0+4rS@c@ZVKLAp`G-sJB$$)^4*A!B zmcf}lIw|VxV9NSoJ8Ag3CwN&d7`|@>&B|l9G8tXT^BDHOUPrtC70NgwN4${$k~d_4 zJ@eo6%YQnOgq$th?0{h`KnqYa$Nz@vlHw<%!C5du6<*j1nwquk=uY}B8r7f|lY+v7 zm|JU$US08ugor8E$h3wH$c&i~;guC|3-tqJy#T;v(g( zBZtPMSyv%jzf->435yM(-UfyHq_D=6;ouL4!ZoD+xI5uCM5ay2m)RPmm$I}h>()hS zO!0gzMxc`BPkUZ)WXaXam%1;)gedA7SM8~8yIy@6TPg!hR0=T>4$Zxd)j&P-pXeSF z9W`lg6@~YDhd19B9ETv(%er^Xp8Yj@AuFVR_8t*KS;6VHkEDKI#!@l!l3v6`W1`1~ zP{C@keuV4Q`Rjc08lx?zmT$e$!3esc9&$XZf4nRL(Z*@keUbk!GZi(2Bmyq*saOD? z3Q$V<*P-X1p2}aQmuMw9nSMbOzuASsxten7DKd6A@ftZ=NhJ(0IM|Jr<91uAul4JR zADqY^AOVT3a(NIxg|U;fyc#ZnSzw2cr}#a5lZ38>nP{05D)7~ad7JPhw!LqOwATXtRhK!w0X4HgS1i<%AxbFmGJx9?sEURV+S{k~g zGYF$IWSlQonq6}e;B(X(sIH|;52+(LYW}v_gBcp|x%rEAVB`5LXg_d5{Q5tMDu0_2 z|LOm$@K2?lrLNF=mr%YP|U-t)~9bqd+wHb4KuPmNK<}PK6e@aosGZK57=Zt+kcszVOSbe;`E^dN! ze7`ha3WUUU7(nS0{?@!}{0+-VO4A{7+nL~UOPW9_P(6^GL0h${SLtqG!} zKl~Ng5#@Sy?65wk9z*3SA`Dpd4b4T^@C8Fhd8O)k_4%0RZL5?#b~jmgU+0|DB%0Z) zql-cPC>A9HPjdOTpPC` zQwvF}uB5kG$Xr4XnaH#ruSjM*xG?_hT7y3G+8Ox`flzU^QIgb_>2&-f+XB6MDr-na zSi#S+c!ToK84<&m6sCiGTd^8pNdXo+$3^l3FL_E`0 z>8it5YIDxtTp2Tm(?}FX^w{fbfgh7>^8mtvN>9fWgFN_*a1P`Gz*dyOZF{OV7BC#j zQV=FQM5m>47xXgapI$WbPM5V`V<7J9tD)oz@d~MDoM`R^Y6-Na(lO~uvZlpu?;zw6 zVO1faor3dg#JEb5Q*gz4<W8tgC3nE2BG2jeIQs1)<{In&7hJ39x=;ih;CJDy)>0S1at*7n?Wr0ahYCpFjZ|@u91Zl7( zv;CSBRC65-6f+*JPf4p1UZ)k=XivKTX6_bWT~7V#rq0Xjas6hMO!HJN8GdpBKg_$B zwDHJF6;z?h<;GXFZan8W{XFNPpOj!(&I1`&kWO86p?Xz`a$`7qV7Xqev|7nn_lQuX ziGpU1MMYt&5dE2A62iX3;*0WzNB9*nSTzI%62A+N?f?;S>N@8M=|ef3gtQTIA*=yq zQAAjOqa!CkHOQo4?TsqrrsJLclXcP?dlAVv?v`}YUjo1Htt;6djP@NPFH+&p1I+f_ z)Y279{7OWomY8baT(4TAOlz1OyD{4P?(DGv3XyJTA2IXe=kqD)^h(@*E3{I~w;ws8 z)ZWv7E)pbEM zd3MOXRH3mQhks9 zv6{s;k0y5vrcjXaVfw8^>YyPo=oIqd5IGI{)+TZq5Z5O&hXAw%ZlL}^6FugH;-%vP zAaKFtt3i^ag226=f0YjzdPn6|4(C2sC5wHFX{7QF!tG1E-JFA`>eZ`}$ymcRJK?0c zN363o{&ir)QySOFY0vcu6)kX#;l??|7o{HBDVJN+17rt|w3;(C_1b>d;g9Gp=8YVl zYTtA52@!7AUEkTm@P&h#eg+F*lR zQ7iotZTcMR1frJ0*V@Hw__~CL>_~2H2cCtuzYIUD24=Cv!1j6s{QS!v=PzwQ(a0HS zBKx04KA}-Ue+%9d`?PG*hIij@54RDSQpA7|>qYVIrK_G6%6;#ZkR}NjUgmGju)2F`>|WJoljo)DJgZr4eo1k1i1+o z1D{>^RlpIY8OUaOEf5EBu%a&~c5aWnqM zxBpJq98f=%M^{4mm~5`CWl%)nFR64U{(chmST&2jp+-r z3675V<;Qi-kJud%oWnCLdaU-)xTnMM%rx%Jw6v@=J|Ir=4n-1Z23r-EVf91CGMGNz zb~wyv4V{H-hkr3j3WbGnComiqmS0vn?n?5v2`Vi>{Ip3OZUEPN7N8XeUtF)Ry6>y> zvn0BTLCiqGroFu|m2zG-;Xb6;W`UyLw)@v}H&(M}XCEVXZQoWF=Ykr5lX3XWwyNyF z#jHv)A*L~2BZ4lX?AlN3X#axMwOC)PoVy^6lCGse9bkGjb=qz%kDa6}MOmSwK`cVO zt(e*MW-x}XtU?GY5}9{MKhRhYOlLhJE5=ca+-RmO04^ z66z{40J=s=ey9OCdc(RCzy zd7Zr1%!y3}MG(D=wM_ebhXnJ@MLi7cImDkhm0y{d-Vm81j`0mbi4lF=eirlr)oW~a zCd?26&j^m4AeXEsIUXiTal)+SPM4)HX%%YWF1?(FV47BaA`h9m67S9x>hWMVHx~Hg z1meUYoLL(p@b3?x|9DgWeI|AJ`Ia84*P{Mb%H$ZRROouR4wZhOPX15=KiBMHl!^JnCt$Az`KiH^_d>cev&f zaG2>cWf$=A@&GP~DubsgYb|L~o)cn5h%2`i^!2)bzOTw2UR!>q5^r&2Vy}JaWFUQE04v>2;Z@ZPwXr?y&G(B^@&y zsd6kC=hHdKV>!NDLIj+3rgZJ|dF`%N$DNd;B)9BbiT9Ju^Wt%%u}SvfM^=|q-nxDG zuWCQG9e#~Q5cyf8@y76#kkR^}{c<_KnZ0QsZcAT|YLRo~&tU|N@BjxOuy`#>`X~Q< z?R?-Gsk$$!oo(BveQLlUrcL#eirhgBLh`qHEMg`+sR1`A=1QX7)ZLMRT+GBy?&mM8 zQG^z-!Oa&J-k7I(3_2#Q6Bg=NX<|@X&+YMIOzfEO2$6Mnh}YV!m!e^__{W@-CTprr zbdh3f=BeCD$gHwCrmwgM3LAv3!Mh$wM)~KWzp^w)Cu6roO7uUG5z*}i0_0j47}pK; ztN530`ScGatLOL06~zO)Qmuv`h!gq5l#wx(EliKe&rz-5qH(hb1*fB#B+q`9=jLp@ zOa2)>JTl7ovxMbrif`Xe9;+fqB1K#l=Dv!iT;xF zdkCvS>C5q|O;}ns3AgoE({Ua-zNT-9_5|P0iANmC6O76Sq_(AN?UeEQJ>#b54fi3k zFmh+P%b1x3^)0M;QxXLP!BZ^h|AhOde*{9A=f3|Xq*JAs^Y{eViF|=EBfS6L%k4ip zk+7M$gEKI3?bQg?H3zaE@;cyv9kv;cqK$VxQbFEsy^iM{XXW0@2|DOu$!-k zSFl}Y=jt-VaT>Cx*KQnHTyXt}f9XswFB9ibYh+k2J!ofO+nD?1iw@mwtrqI4_i?nE zhLkPp41ED62me}J<`3RN80#vjW;wt`pP?%oQ!oqy7`miL>d-35a=qotK$p{IzeSk# ze_$CFYp_zIkrPFVaW^s#U4xT1lI^A0IBe~Y<4uS%zSV=wcuLr%gQT=&5$&K*bwqx| zWzCMiz>7t^Et@9CRUm9E+@hy~sBpm9fri$sE1zgLU((1?Yg{N1Sars=DiW&~Zw=3I zi7y)&oTC?UWD2w97xQ&5vx zRXEBGeJ(I?Y}eR0_O{$~)bMJRTsNUPIfR!xU9PE7A>AMNr_wbrFK>&vVw=Y;RH zO$mlpmMsQ}-FQ2cSj7s7GpC+~^Q~dC?y>M}%!-3kq(F3hGWo9B-Gn02AwUgJ>Z-pKOaj zysJBQx{1>Va=*e@sLb2z&RmQ7ira;aBijM-xQ&cpR>X3wP^foXM~u1>sv9xOjzZpX z0K;EGouSYD~oQ&lAafj3~EaXfFShC+>VsRlEMa9cg9i zFxhCKO}K0ax6g4@DEA?dg{mo>s+~RPI^ybb^u--^nTF>**0l5R9pocwB?_K)BG_)S zyLb&k%XZhBVr7U$wlhMqwL)_r&&n%*N$}~qijbkfM|dIWP{MyLx}X&}ES?}7i;9bW zmTVK@zR)7kE2+L42Q`n4m0VVg5l5(W`SC9HsfrLZ=v%lpef=Gj)W59VTLe+Z$8T8i z4V%5+T0t8LnM&H>Rsm5C%qpWBFqgTwL{=_4mE{S3EnBXknM&u8n}A^IIM4$s3m(Rd z>zq=CP-!9p9es2C*)_hoL@tDYABn+o#*l;6@7;knWIyDrt5EuakO99S$}n((Fj4y} zD!VvuRzghcE{!s;jC*<_H$y6!6QpePo2A3ZbX*ZzRnQq*b%KK^NF^z96CHaWmzU@f z#j;y?X=UP&+YS3kZx7;{ zDA{9(wfz7GF`1A6iB6fnXu0?&d|^p|6)%3$aG0Uor~8o? z*e}u#qz7Ri?8Uxp4m_u{a@%bztvz-BzewR6bh*1Xp+G=tQGpcy|4V_&*aOqu|32CM zz3r*E8o8SNea2hYJpLQ-_}R&M9^%@AMx&`1H8aDx4j%-gE+baf2+9zI*+Pmt+v{39 zDZ3Ix_vPYSc;Y;yn68kW4CG>PE5RoaV0n@#eVmk?p$u&Fy&KDTy!f^Hy6&^-H*)#u zdrSCTJPJw?(hLf56%2;_3n|ujUSJOU8VPOTlDULwt0jS@j^t1WS z!n7dZIoT+|O9hFUUMbID4Ec$!cc($DuQWkocVRcYSikFeM&RZ=?BW)mG4?fh#)KVG zcJ!<=-8{&MdE)+}?C8s{k@l49I|Zwswy^ZN3;E!FKyglY~Aq?4m74P-0)sMTGXqd5(S<-(DjjM z&7dL-Mr8jhUCAG$5^mI<|%`;JI5FVUnNj!VO2?Jiqa|c2;4^n!R z`5KK0hyB*F4w%cJ@Un6GC{mY&r%g`OX|1w2$B7wxu97%<@~9>NlXYd9RMF2UM>(z0 zouu4*+u+1*k;+nFPk%ly!nuMBgH4sL5Z`@Rok&?Ef=JrTmvBAS1h?C0)ty5+yEFRz zY$G=coQtNmT@1O5uk#_MQM1&bPPnspy5#>=_7%WcEL*n$;t3FUcXxMpcXxMpA@1(( z32}FUxI1xoH;5;M_i@j?f6mF_p3Cd1DTb=dTK#qJneN`*d+pvYD*L?M(1O%DEmB>$ zs6n;@Lcm9c7=l6J&J(yBnm#+MxMvd-VKqae7;H7p-th(nwc}?ov%$8ckwY%n{RAF3 zTl^SF7qIWdSa7%WJ@B^V-wD|Z)9IQkl$xF>ebi>0AwBv5oh5$D*C*Pyj?j_*pT*IMgu3 z$p#f0_da0~Wq(H~yP##oQ}x66iYFc0O@JFgyB>ul@qz{&<14#Jy@myMM^N%oy0r|b zDPBoU!Y$vUxi%_kPeb4Hrc>;Zd^sftawKla0o|3mk@B)339@&p6inAo(Su3qlK2a) zf?EU`oSg^?f`?y=@Vaq4Dps8HLHW zIe~fHkXwT>@)r+5W7#pW$gzbbaJ$9e;W-u#VF?D=gsFfFlBJ5wR>SB;+f)sFJsYJ| z29l2Ykg+#1|INd=uj3&d)m@usb;VbGnoI1RHvva@?i&>sP&;Lt!ZY=e!=d-yZ;QV% zP@(f)+{|<*XDq%mvYKwIazn8HS`~mW%9+B|`&x*n?Y$@l{uy@ z^XxQnuny+p0JG0h)#^7}C|Btyp7=P#A2ed1vP0KGw9+~-^y4~S$bRm3gCT{+7Z<(A zJ&tg=7X|uKPKd6%z@IcZ@FgQe=rS&&1|O!s#>B_z!M_^B`O(SqE>|x- zh{~)$RW_~jXj)}mO>_PZvGdD|vtN44=Tp!oCP0>)gYeJ;n*&^BZG{$>y%Yb|L zeBUI#470!F`GM-U$?+~k+g9lj5C-P_i1%c3Zbo!@EjMJDoxQ7%jHHKeMVw&_(aoL? z%*h*aIt9-De$J>ZRLa7aWcLn<=%D+u0}RV9ys#TBGLAE%Vh`LWjWUi`Q3kpW;bd)YD~f(#$jfNdx}lOAq=#J*aV zz;K>I?)4feI+HrrrhDVkjePq;L7r87;&vm|7qaN z_>XhM8GU6I5tSr3O2W4W%m6wDH#=l32!%LRho(~*d3GfA6v-ND^0trp-qZs(B(ewD z3y3@ZV!2`DZ6b6c(Ftqg-s715;=lZqGF>H+z+c&7NeDz!We+7WNk>X*b7OZmlcTnf z{C1CB67e@xbWprDhN+t!B%4od#|>yQA$5mBM>XdhP?1U^%aD&^=PYWQEY*8Mr%h~R zOVzrd9}6RSl}Lt42r166_*s|U<1}`{l(H}m8H=D+oG>*=+=W^%IMB&CHZ-?)78G2b z)9kj_ldMecB_65eV&R+(yQ$2`ol&&7$&ns_{%A6cC2C*C6dY7qyWrHSYyOBl$0=$> z-YgkNlH{1MR-FXx7rD=4;l%6Ub3OMx9)A|Y7KLnvb`5OB?hLb#o@Wu(k|;_b!fbq( zX|rh*D3ICnZF{5ipmz8`5UV3Otwcso0I#;Q(@w+Pyj&Qa(}Uq2O(AcLU(T`+x_&~?CFLly*`fdP6NU5A|ygPXM>}(+) zkTRUw*cD<% zzFnMeB(A4A9{|Zx2*#!sRCFTk2|AMy5+@z8ws0L-{mt(9;H#}EGePUWxLabB_fFcp zLiT)TDLUXPbV2$Cde<9gv4=;u5aQ$kc9|GE2?AQZsS~D%AR`}qP?-kS_bd>C2r(I; zOc&r~HB7tUOQgZOpH&7C&q%N612f?t(MAe(B z@A!iZi)0qo^Nyb`#9DkzKjoI4rR1ghi1wJU5Tejt!ISGE93m@qDNYd|gg9(s|8-&G zcMnsX0=@2qQQ__ujux#EJ=veg&?3U<`tIWk~F=vm+WTviUvueFk&J@TcoGO{~C%6NiiNJ*0FJBQ!3Ab zm59ILI24e8!=;-k%yEf~YqN_UJ8k z0GVIS0n^8Yc)UK1eQne}<0XqzHkkTl*8VrWr zo}y?WN5@TL*1p>@MrUtxq0Vki($sn_!&;gR2e$?F4^pe@J_BQS&K3{4n+f7tZX4wQn z*Z#0eBs&H8_t`w^?ZYx=BGgyUI;H$i*t%(~8BRZ4gH+nJT0R-3lzdn4JY=xfs!YpF zQdi3kV|NTMB}uxx^KP!`=S(}{s*kfb?6w^OZpU?Wa~7f@Q^pV}+L@9kfDE`c@h5T* zY@@@?HJI)j;Y#l8z|k8y#lNTh2r?s=X_!+jny>OsA7NM~(rh3Tj7?e&pD!Jm28*UL zmRgopf0sV~MzaHDTW!bPMNcymg=!OS2bD@6Z+)R#227ET3s+2m-(W$xXBE#L$Whsi zjz6P+4cGBQkJY*vc1voifsTD}?H$&NoN^<=zK~75d|WSU4Jaw`!GoPr$b>4AjbMy+ z%4;Kt7#wwi)gyzL$R97(N?-cKygLClUk{bBPjSMLdm|MG-;oz70mGNDus zdGOi}L59=uz=VR2nIux^(D85f)1|tK&c!z1KS6tgYd^jgg6lT^5h42tZCn#Q-9k>H zVby-zby2o_GjI!zKn8ZuQ`asmp6R@=FR9kJ_Vja#I#=wtQWTes>INZynAoj$5 zN^9Ws&hvDhu*lY=De$Zby12$N&1#U2W1OHzuh;fSZH4igQodAG1K*;%>P9emF7PPD z>XZ&_hiFcX9rBXQ8-#bgSQ!5coh=(>^8gL%iOnnR>{_O#bF>l+6yZQ4R42{Sd#c7G zHy!)|g^tmtT4$YEk9PUIM8h)r?0_f=aam-`koGL&0Zp*c3H2SvrSr60s|0VtFPF^) z-$}3C94MKB)r#398;v@)bMN#qH}-%XAyJ_V&k@k+GHJ^+YA<*xmxN8qT6xd+3@i$( z0`?f(la@NGP*H0PT#Od3C6>0hxarvSr3G;0P=rG^v=nB5sfJ}9&klYZ>G1BM2({El zg0i|%d~|f2e(yWsh%r)XsV~Fm`F*Gsm;yTQV)dW!c8^WHRfk~@iC$w^h=ICTD!DD;~TIlIoVUh*r@aS|%Ae3Io zU~>^l$P8{6Ro~g26!@NToOZ(^5f8p`*6ovpcQdIDf%)?{NPPwHB>l*f_prp9XDCM8 zG`(I8xl|w{x(c`}T_;LJ!%h6L=N=zglX2Ea+2%Q8^GA>jow-M>0w{XIE-yz|?~M+; zeZO2F3QK@>(rqR|i7J^!1YGH^9MK~IQPD}R<6^~VZWErnek^xHV>ZdiPc4wesiYVL z2~8l7^g)X$kd}HC74!Y=Uq^xre22Osz!|W@zsoB9dT;2Dx8iSuK!Tj+Pgy0-TGd)7 zNy)m@P3Le@AyO*@Z2~+K9t2;=7>-*e(ZG`dBPAnZLhl^zBIy9G+c)=lq0UUNV4+N% zu*Nc4_cDh$ou3}Re}`U&(e^N?I_T~#42li13_LDYm`bNLC~>z0ZG^o6=IDdbIf+XFTfe>SeLw4UzaK#4CM4HNOs- zz>VBRkL@*A7+XY8%De)|BYE<%pe~JzZN-EU4-s_P9eINA^Qvy3z?DOTlkS!kfBG_7 zg{L6N2(=3y=iY)kang=0jClzAWZqf+fDMy-MH&Px&6X36P^!0gj%Z0JLvg~oB$9Z| zgl=6_$4LSD#(2t{Eg=2|v_{w7op+)>ehcvio@*>XM!kz+xfJees9(ObmZ~rVGH>K zWaiBlWGEV{JU=KQ>{!0+EDe-+Z#pO zv{^R<7A^gloN;Tx$g`N*Z5OG!5gN^Xj=2<4D;k1QuN5N{4O`Pfjo3Ht_RRYSzsnhTK?YUf)z4WjNY z>R04WTIh4N(RbY*hPsjKGhKu;&WI)D53RhTUOT}#QBDfUh%lJSy88oqBFX)1pt>;M z>{NTkPPk8#}DUO;#AV8I7ZQsC?Wzxn|3ubiQYI|Fn_g4r)%eNZ~ zSvTYKS*9Bcw{!=C$=1` zGQ~1D97;N!8rzKPX5WoqDHosZIKjc!MS+Q9ItJK?6Wd%STS2H!*A#a4t5 zJ-Rz_`n>>Up%|81tJR2KND<6Uoe82l={J~r*D5c_bThxVxJ<}?b0Sy}L1u|Yk=e&t z0b5c2X(#x^^fI)l<2=3b=|1OH_)-2beVEH9IzpS*Es0!4Or+xE$%zdgY+VTK2}#fpxSPtD^1a6Z)S%5eqVDzs`rL1U;Zep@^Y zWf#dJzp_iWP{z=UEepfZ4ltYMb^%H7_m4Pu81CP@Ra)ds+|Oi~a>Xi(RBCy2dTu-R z$dw(E?$QJUA3tTIf;uZq!^?_edu~bltHs!5WPM-U=R74UsBwN&nus2c?`XAzNUYY|fasp?z$nFwXQYnT`iSR<=N`1~h3#L#lF-Fc1D#UZhC2IXZ{#IDYl_r8 z?+BRvo_fPGAXi+bPVzp=nKTvN_v*xCrb^n=3cQ~No{JzfPo@YWh=7K(M_$Jk*+9u* zEY4Ww3A|JQ`+$z(hec&3&3wxV{q>D{fj!Euy2>tla^LP_2T8`St2em~qQp zm{Tk<>V3ecaP1ghn}kzS7VtKksV*27X+;Y6#I$urr=25xuC=AIP7#Jp+)L67G6>EZ zA~n}qEWm6A8GOK!3q9Yw*Z07R(qr{YBOo5&4#pD_O(O^y0a{UlC6w@ZalAN0Rq_E0 zVA!pI-6^`?nb7`y(3W5OsoVJ^MT!7r57Jm{FS{(GWAWwAh$dBpffjcOZUpPv$tTc} zv~jnA{+|18GmMDq7VK6Sb=-2nzz^7TDiixA{mf%8eQC|x>*=)((3}twJCoh~V4m3) zM5fwDbrTpnYR`lIO7Il7Eq@)St{h>Nllv+5Hk2FAE8fdD*YT|zJix?!cZ-=Uqqieb z-~swMc+yvTu(h?fT4K_UuVDqTup3%((3Q!0*Tfwyl`3e27*p{$ zaJMMF-Pb=3imlQ*%M6q5dh3tT+^%wG_r)q5?yHvrYAmc-zUo*HtP&qP#@bfcX~jwn!$k~XyC#Ox9i7dO7b4}b^f zrVEPkeD%)l0-c_gazzFf=__#Q6Pwv_V=B^h=)CYCUszS6g!}T!r&pL)E*+2C z5KCcctx6Otpf@x~7wZz*>qB_JwO!uI@9wL0_F>QAtg3fvwj*#_AKvsaD?!gcj+zp) zl2mC)yiuumO+?R2`iiVpf_E|9&}83;^&95y96F6T#E1}DY!|^IW|pf-3G0l zE&_r{24TQAa`1xj3JMev)B_J-K2MTo{nyRKWjV#+O}2ah2DZ>qnYF_O{a6Gy{aLJi#hWo3YT3U7yVxoNrUyw31163sHsCUQG|rriZFeoTcP` zFV<&;-;5x0n`rqMjx2^_7y)dHPV@tJC*jHQo!~1h`#z)Gu7m@0@z*e?o|S#5#Ht~%GC|r zd?EY_E0XKUQ2o7*e3D9{Lt7s#x~`hjzwQ{TYw;Fq8la&)%4Vj_N@ivmaSNw9X3M$MAG97a&m1SODLZ-#$~7&@ zrB~0E+38b6sfezlmhDej*KRVbzptE0Xg%$xpjqoeL;-LwmKIR#%+EZ7U|&;9rS6lo8u9iOD;-3HF{Gm=EL@W zG8L9&8=FxGHICO+MX@lC?DpY4GAE9!S+7hKsTmr8%hFI9QGI4sCj&?Of-yA98KvLsP z|k5cP?Z zay4&3t8e5RgA_@c7z{RX6d`;{B~l03#AD@RJD1{;4x93d7mD15wnFLi^LI%`Z~6@ zq9}|AG1Lq-1~Fb{1b?}bFLaSnWm!7L)P8#%g{{}}u@Q`4N{s3LiD4kSqTnM8UNN4XQi57LZRzkkL9+rJ{_?juO;cZL=MIT2H1q-=Tt1G666hVaPojp^(AM>6 zDQQf0_>1u=rvT+6(5 zAQR5%mlLdhkl4MpIyY0GN9VrGYkq?1sF8F(VeB0u3{p`h6IgEBC}Jr!^-)@5@<8s( zXyiL`ENayjlbGx}3q2T;y&|@~&$+T=hN0iS4BAARQ_JBclEeBW7}$3lx|!Ee&vs&o z=A4b##+t=rylLD-dc(X)^d?KbmU^9uZ)zXbIPC%pD{s(>p9*fu8&(?$LE67%%b-e) z!IU|lpUpK`<&YPqJnj5wb8(;a)JoC~+Kb`Fq-HL<>X@DYPqu4t9tLfS9C>Kn*Ho zl3Zz2y8;bCi@KYchQ;1JTPXL`ZMCb4R7fLlP_qKJ`aTs3H2Q6`g3GdtURX%yk`~xS z#|RDc0Y|%b+$^QYCSEG~ZF;*rT;@T=Ko6uwRJ&RasW^4$W<^nS^v|}UmIHe`P{(x| zI&y@A&b6=G2#r*st8^|19`Yw20=}MF9@@6zIuB%!vd7J%E|@zK(MRvFif-szGX^db zIvb}^{t9g(lZhLP&h6;2p>69mWE3ss6di_-KeYjPVskOMEu?5m_A>;o`6 z5ot9G8pI8Jwi@yJExKVZVw-3FD7TW3Ya{_*rS5+LicF^BX(Mq)H&l_B5o9^ zpcL6s^X}J-_9RAs(wk7s1J$cjO~jo*4l3!1V)$J+_j7t8g4A=ab`L(-{#G?z>z@KneXt&ZOv>m);*lTA}gRhYxtJt;0QZ<#l+OWu6(%(tdZ`LkXb}TQjhal;1vd{D+b@g7G z25i;qgu#ieYC?Fa?iwzeLiJa|vAU1AggN5q{?O?J9YU|xHi}PZb<6>I7->aWA4Y7-|a+7)RQagGQn@cj+ED7h6!b>XIIVI=iT(

    xR8>x!-hF($8?9?2$_G0!Ov-PHdEZo(@$?ZcCM)7YB>$ZH zMWhPJRjqPm%P_V5#UMfZ_L}+C(&-@fiUm`Gvj-V2YSM@AwZ4+@>lf-7*yxYxYzJG9 z8Z>T-V-h|PI-K8#1LBs++!+=;G&ed}>Qgs%CA|)bQd$SYzJ8U?H+Pb2&Bf=hSo*HL zELt9Z&2dz8&QQ^NY<~PP+wu57Eu>N@zkBFwO!w+BO}S0Xa(XN?BY)~WGZ<~bbZC&C zlJR|EK1_BLx*FK@OvkyG#ANGZbW~h5*xsx24d9toyTm-JUKo$r%(W42t>}}xax;qL zaw}VpEIzc=)VsC}Yx9kb@Fhh4bEWXlb4-DIH+tzLMlaT-I#A!e zKkZtQ^c@m*;P`&@?i@8tZ&Nel~z27L^F*m1}Rg^-xTzqy}3Mmq4jjJ zJC;ZK#U6QdBoE~b+-^xIyHSxNAYFGGB2WifSL_@3*CnzN18{kDvLM;dN50Jan0*YL zysmN}*Wyag#N?qeBO*E})kZMhzVKMFI zDJmEG_Wsed#Z_9T6Bi+-#s5oCG_$W<;8y%ubb!E>m!Z=HcX$Bn<&6a4a2Chp>^pAB zp^7;RF-lQa$1Ct5l88Ak4)(sYu$IRd5RwLPKa|y3wT%gBAk>pg*z=8s4UmZK(jK)g9^;e+#jYwF69JTFlz)U-(XXg zVD)U0B}ikjXJzsrW~I@l1yli*n|ww}_xpCY3<26Dc~n-dpoOqM{Yl-J@$IpVw7>YtzDZx zm}rqKSP(PM@M<^E+@ndf@wwxe$H(}rbzF`SGkwj1!{}Q6TTpZBhPDXdbCOaApGUN{ zp2q!e{c-`;@|>B9}2F<0G^h<$k%JitT<6nO`x0+K5ENk(~hYea8D*w-By=7s}!4= zEoMdOGi9B3%80sqaGRk?gj6fRr0Fa>BuM;1>R*i3bMU5rwG3r+@a~dnKMBZ_F6p*D zSRYfrDus5nFWJ%X>N6PgH~k zoB<3qHH^YyRy53{hNY>5xN6Eca!2jh-~3)NhoknTATWJ!&07-OYK-DUfkw!51UCML zP%@F<)A4~r{TkOKV9%x#edO(7H_Ke!J~A!tmmodA8dcLhhp0O@++ z35`8{H{So#b*sdgj8}LRCS%J zMNaioFbuoChaX&t7Y?OKWH~o|eKoy3#xH1@U=XTh@!Q~vn|%by)=@}Z~4PJ z#rEgEqtziT(C6b(ZY(f6TML12y;4W&hc|Wk^qF-Z1s^|{r;$!-$%|%?L5*qkt|0_#E8Vm^z>=DH zA)i=K;T0iy&HZUpgwtjWd=X{jWOQ{Vfx1iEWh^jM_jtfULMGKh;?UFn9d2W&&uVkI znCG!maf1t{Up0-*%Tdhm0F4C37_#;%@ma4c@(iAP_aZ){`hdlr=SCOwrW zCS`?8iWZGp-Jd2JaP~we_KLo04??+L+utj7_Ns~95mHW&?m6N)fbK6{TH82eKPdw* zyvp48VDX+auZ&A=LBr9ZzGzH+JHsC3p)|Bj{LquB=03Jv#0I!^36fe2=|kle_y}%Y zZMUr8YRuvpM(Yn?ik*}SUI%Qksmt(!<}vZl9k#%ZmL*phd>@;KK(izsGu1Pw3@gi% z8p#5HtQ8`>v<~M9-&pH{t`g;c>K?mcz8tk)kZB8|dc;byKSO&A!E(z=xHg{sp{>G+ zouA_g>SkebBfF}|RJUj274Y^1>;6s-eX)HzLvOD>Y1B#-Z854a=er5qqP4DvqU1IL z@VWKv&GuY%VqR$Y*Q&i3TF>jL@Uz_aKXQO$@3>X%wo>f-m<~=ye(bo_NNgIUKCT^* z3um;yNvFYd2dz%BImY}j_l*DvAuvj3Ev^cyap}Y4*`r*cE2i-e{jAGR`}Mk3WH}a5 zZ?mR>|=Izi2&RGE4_MJ(~Dz6D>7h=alt^eb2+Vd5Zh# zp`ZKBEzPQQHhds7y$?({(za}(Eve7P)~cR7yl$!N-j!maYX4zTjm{bu4*V@u)GYCA zM4{J97aDL`0J*tw;)~ZEF#Tb49m(s})Pxg}Nd_LQK2|8U9)fM!kz0rtUWz7dL{eUi zA(b07DqfmE9{hbrwrw#y?>ka@(p<#%J;XUWD6y;uZzKIrj231k^Xv>aV8O>(sDfCg@6$-_BI1rTWK3XbZ0xiZX`!QGFhWH$?;sOH?B<_4`KXd2TyX zViEvhZ!60PDc_QlVMh@e4$G?8P#0=6f2ve4d0S>Azth>50p#~Cx_~lOT&)vK%v9Mz z9J4WWMsU+Uul}8}SS9#=J9-0CXJo`-pjDLU{>Ut8dKIHMr}mW4{g_CwL^6n^%lNrb zN!T9a5yXWgpW9HnvbeE=II_8QZSPJxkw0IYBm}N!rT;bC8HRp?=|!5H)2+jsgyiqRIXnfwga8gMYN&vNAS~9r)D$peKR(j{E{TdRFU#B z<;Vl20JSOBn1$@~*W?Zk!!15f4HO>})HqKDn9MIH(`G?tN}H#xiehlE(3um>iCb$N zLD+Q@#TMJT8(G@h4UmfJ2+Ox`jD@Re{595tBwu5LH=ttNH@_8_$z5^-t4Cyf*bi)u ztx%NyZm=*{*DMOO^o6gJmm@E+WRd8yRwGaR^akm04&0lK=jL?hhqr%e6Mwx?Ws&JD zaQ5_EPnl}{ZoPhs$$2Ev?e{KIke~}D2u(QPJLV%&5@#~7@6T1jfD9g!cQaM9JgX&|LGoQE{Lh@=M65w z9alK+Q1=Ih4>Sg+ZLzH&q|WF$&FbK5JpOv|ddHyKj)r~3TH&<^x)VSPx8`PQ35i7NJ=jp(aN%iIR}7#z`P(|}jD1o% zZF9~T^QZ0Fdqv{mM8A#sSiZ(v9LGKCOtm-kiVCd#@<6s%wu#1Q1#=~%w> zrl?pthDR))hp&>qly?jMHL=53fPJ`lM?glcJuEH}CM{V{6U>hf73S~4!KXMEw^&Y7 z4{w&iLu_}AAbxDH1M=J~?GrWLND238JO$zVat1B%^L*33e$7|XA zls1r#cuaQ>#;0;+D!~HTl_8AL&$j%g1Kx7v24#aF{Q+p+h31$*S9%rXT9jjF=TNc( z23%Sr1IG1osJ(uAL_m04g~L~_ZYydDSj5l zGP6t#d5z@uBUZa|u?}9>N3u}1gNGOygP5L5Cxf4go3x?Kq#b7GTk=gZnnUuN++0zn z27%%V!d$FubU`2K2%!}ctgD)j;4nflhF2PE(VywWALKM&Bd+m+2=?>R0Il#dv;m)5 zts4r(Yp$l4crwsdomvk;s7a)g6-~uvQR3Y?Ik8WR*yTg??;)sRiuEjn-If_YydA%m z@wRljzltj_#crXi3e*T*B9(2_xD4t6{=Vn7Z$-=5jeAG2;u_ib`CIw}_3i1&CW+@f zX(6!tCnX8~j$!`DJUo6vF#C%afu3<0ZHR4vJx?6K84-%V@7nxrT>s+`+#jQRguME{ zj)XKcQl8)yXdv*CAm>mHg(A1flmgS@n)c*_`dRa{s|H#)r>#)JdP9yAb=+o$h(!x{ zUIRALkEsd}L_Jb6SRXRZJl0t0KmG9d@k$4loYX)@MpgpXm+$>OO;+wsU}%~sMSk>$ z%sxsAB3pH@vyV;WpKi8m@;5s|!64z>M=WfWc?)ZXuaj55`WGwvA5oI;7ejXIX$@~c z8nt*O`PL3n@K?G;R)z1-6%dGZ!D*@TGHA~$z^KL_W-Su$|ysw+^L+E~k@$rgI{Q!?8-0E!8 zxM1)H2Ia=)v|0=5#_nsENYw|{A9NH0eDY*iW-h?79B5slt`(DXoRbW$9~>amy7XH( zR-_o?F9f>fNlmVQ^tlEa>bob+eGEz(iwrysCSL_qHaOvz>oZ6-<@`Yk78*~=-Hf$7iBwJ~-ifEs1-!r|d|(zgR~z=> zIInVoYz>zLUx*dIZu&Jxh2EDv?C$#LQdB!Yf)-q_53BkF4K;_jvD{(WFzkHqQ9ZE( z<%u`;VW(gpeXol(ZIc;%&59NBvTpl}`LN(IXOb3Y`bn`aN{<|3e{9BH#Zzp66|u)| z>Do<1WAqZyBC5Fv!I~<^5quNgk63qfCf|)FV#V)}!AAc&xWZuMf$Ct)-zP^xj()iw z>-*+o^?QRy{iMFTcM%H>ovhdiFL(aKco{7`0B1p=0B1qje(@IAS(_Q^JN%B4Y(}iO zbQcdoz&Hr703cSVJNNiAFdDq$7QSpac`gCU4L^G#tz{7O8;Bob%0yI;ubxP@5K3t0 z1-2+o57JrJE}aUk&!{VbuB+8~kkDN%cB>PFNrO%>oWK|0VIe(*M3l{){UzjE(yNx? za6e&zYF1dO&M}XviL;G-(iao>Hb1hTi2@U;Cg<8vlze2rbP=$k^wo!bQ6!6;@-~~) z??Zr9ow zA=l~)->N9Co}($XV}|D~o6=y>dJmYt?dtS?7h%KVm*EViR=vieKx2H$jfN_7sarUf zmSPznK6b+CmpQ@@2_jz$Z;uI8h*b0{FAUxTVwhGVYU5Jv&=!=^lYd%!U+i^irr>bM zzS-;46hU%`k9W?*#aA!loZ^7kQ-1d8BjD@C`u9G4nf&WdYnK}MH0^Y2s{gf9993(*A|G`f;iqo97N*~28;L6JPpJBBH4?^SgR5% zu%Yg3cJXp&_F-)NWGW0&J!R=tA3n=wK`qsRV6vO2y`u-y#hGk}Ulzti1=T!l`GPJS z=G4qAj~5F6ni1Vl57OFmut_+3a`qw0K}a<${V#*R`Rh!Ar%Rgw)+{Uc~8t-%Ihbq z-j+|>cbi;~yfyxkl4}LS^4QNXjSeB$4N@c%^hvmKtx z0pRve5B^)M{%_1@ZfZ$qfJ)8)TIgpItLK6NcyoUNz-Mjk@Ka&lMpD<*3J{3+tSkSr zZYI74MtK0d8Nh}Aj0?C^0))Z*0$Ko|4`5-fYw#Ztx|e`M)@=6g0nNk%s4v4`0NDV3 zk$(aNj2kYlyp9eg0Cite{bxChmkiMtuw(CkDy9OY{&D}pkOpXIL^z{~#&0%1E{ zK>kKWfRLbwwWXniwY9mU&99s0sLU*`5Fi`R0H`V1bHxF7)Oh~@{qLkxKW*>VxO>Mc z_9Xz6CBOv$`cuIK{DNOpS@b_v_iMb2Qk2^-fHr0VWM=p)9vIcH@vQ6}bS*6Yn+<0` zHS-Vv-qdTr#{}n3wF3e|XZ$C;U)Qd{m8L}r&_O_ewZqTP@pJJM`6Zf!wef%L?Uz~3 zpTS_ne+l+mInQ6()XNOo&n#$?|C{C4&G0hQ=rg7e;4A)%PJcP|_)Ff=moW%6^ug z8A_gu6#(#0?fWxw=jFpM^OZb5obmUE|C2J}zt06c~G6javMT=uh?kFRJn{;a>`(Kf~)={S*9)sq#zMmpb6ju-(@G1p8+%!%NJUqO#AJ zLyrH1`9}=EfBQ1Nly7}TZE*Sx)c-E#`m*{jB`KeY#NB?E=#S?4w?O4ff|v4t&jdW4 zzd`U1Vt_B1UW$Z0Gx_`c2GegzhP~u`sr&TIN$CF@od2W(^^)qPP{uQrcGz!F{ex`A zOQx5i1kX&Gk-x$8hdJ>6Qlj7`)yr7$XDZp4-=+e5Uu^!Y>-Li5WoYd)iE;dIll<|% z{z+`)CCkeg&Sw^b#NTH5b42G$f|v1g&jg|=|DOc^tHoYMG(A({rT+%i|7@$5p)Jq& zu9?4q|IdLgFWc>9B)~ISBVax9V!-~>SoO!R`1K^~<^J \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 000000000..e95643d6a --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/project/build.properties b/project/build.properties index b7dd3cb2a..394cb75cf 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.0.2 +sbt.version=1.0.4 diff --git a/scripts/script-helper b/scripts/script-helper new file mode 100644 index 000000000..ac39d9a51 --- /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 = 9* ]] ; then + echo "The build is using Java 9 ($java_version). We need additional JVM parameters" + export _JAVA_OPTIONS="$_JAVA_OPTIONS --add-modules=java.xml.bind" +else + echo "The build is NOT using Java 9 ($java_version). No addional JVM params needed." +fi diff --git a/scripts/test-gradle b/scripts/test-gradle new file mode 100755 index 000000000..298c7ebe1 --- /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 test -i --stacktrace diff --git a/scripts/test-sbt b/scripts/test-sbt new file mode 100755 index 000000000..0425367b1 --- /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 test From b8829e81d3e3c2ad16bf75f4fc181a4162ecbff1 Mon Sep 17 00:00:00 2001 From: Greg Methvin Date: Fri, 22 Dec 2017 04:02:12 -0800 Subject: [PATCH 43/66] Updated with template-control on 2017-12-21T20:59:17.064Z (#55) **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.10") --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 51cd14953..bd7231813 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,2 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.9") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.10") From 5388ad2bb112433d75b227ead88b1986f371209e Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Tue, 2 Jan 2018 15:46:07 -0200 Subject: [PATCH 44/66] Upgrade branch 2.6.x using TemplateControl (#56) * Updated with template-control on 2017-12-22T16:49:12.374Z **/test-gradle: ./gradlew -Dscala.binary.version=$scala_binary_version check -i --stacktrace * Add run permission to test-gradle script --- scripts/test-gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/test-gradle b/scripts/test-gradle index 298c7ebe1..84a051a20 100755 --- a/scripts/test-gradle +++ b/scripts/test-gradle @@ -10,4 +10,4 @@ scala_binary_version=$(echo $TRAVIS_SCALA_VERSION | cut -c1-4) echo "+------------------------------+" echo "| Executing tests using Gradle |" echo "+------------------------------+" -./gradlew -Dscala.binary.version=$scala_binary_version test -i --stacktrace +./gradlew -Dscala.binary.version=$scala_binary_version check -i --stacktrace From 920c9f26111426b6919f16633b89ccf62a66832b Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Tue, 9 Jan 2018 21:02:40 -0200 Subject: [PATCH 45/66] Upgrade branch 2.6.x using TemplateControl (#57) * Updated with template-control on 2018-01-09T20:17:22.737Z **/build.properties: sbt.version=1.1.0 **build.gradle: def playVersion = "2.6.10" * test-gradle-permissions --- build.gradle | 2 +- project/build.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index b286f5cd9..f07837290 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ plugins { id 'idea' } -def playVersion = '2.6.9' +def playVersion = "2.6.10" def scalaVersion = System.getProperty("scala.binary.version", /* default = */ "2.12") model { diff --git a/project/build.properties b/project/build.properties index 394cb75cf..8b697bbb9 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.0.4 +sbt.version=1.1.0 From 3cc8ab2924ae331444b2e51c9310888ee6769588 Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Thu, 11 Jan 2018 17:15:23 -0200 Subject: [PATCH 46/66] Fix cross build to sbt 1.1.0 (#58) --- build.sbt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.sbt b/build.sbt index 4f6f7b6c6..fa139d159 100644 --- a/build.sbt +++ b/build.sbt @@ -6,4 +6,6 @@ lazy val root = (project in file(".")).enablePlugins(PlayScala) scalaVersion := "2.12.4" +crossScalaVersions := Seq("2.11.12", "2.12.4") + libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "3.1.2" % Test From 68f54bc230f32b1ffcac7aa538a9bc038f1c6c98 Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Fri, 12 Jan 2018 12:19:54 -0200 Subject: [PATCH 47/66] Upgrade branch 2.6.x using TemplateControl (#59) * Updated with template-control on 2018-01-11T21:32:27.158Z **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.11") **build.gradle: def playVersion = "2.6.11" * test-gradle-permissions --- build.gradle | 2 +- project/plugins.sbt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index f07837290..c308ec89d 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ plugins { id 'idea' } -def playVersion = "2.6.10" +def playVersion = "2.6.11" def scalaVersion = System.getProperty("scala.binary.version", /* default = */ "2.12") model { diff --git a/project/plugins.sbt b/project/plugins.sbt index bd7231813..1ae01abd2 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,2 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.10") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.11") From 2c08658fc945693d592e285720d511b0aca86f02 Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Fri, 2 Mar 2018 18:19:11 -0300 Subject: [PATCH 48/66] Updated with template-control on 2018-03-02T18:56:27.945Z (#60) **/build.properties: sbt.version=1.1.1 **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.12") **build.gradle: def playVersion = "2.6.12" --- build.gradle | 2 +- project/build.properties | 2 +- project/plugins.sbt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index c308ec89d..8e99b8878 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ plugins { id 'idea' } -def playVersion = "2.6.11" +def playVersion = "2.6.12" def scalaVersion = System.getProperty("scala.binary.version", /* default = */ "2.12") model { diff --git a/project/build.properties b/project/build.properties index 8b697bbb9..31334bbd3 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.1.0 +sbt.version=1.1.1 diff --git a/project/plugins.sbt b/project/plugins.sbt index 1ae01abd2..743bbae21 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,2 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.11") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.12") From 65e6f6475fed97d46eb25d2c7e1e1a2cfb5e604c Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Sat, 7 Apr 2018 10:51:59 -0300 Subject: [PATCH 49/66] Updated with template-control on 2018-04-06T19:34:58.408Z (#61) **/build.properties: sbt.version=1.1.2 **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.13") **build.gradle: def playVersion = "2.6.13" --- build.gradle | 2 +- project/build.properties | 2 +- project/plugins.sbt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 8e99b8878..8863d25fa 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ plugins { id 'idea' } -def playVersion = "2.6.12" +def playVersion = "2.6.13" def scalaVersion = System.getProperty("scala.binary.version", /* default = */ "2.12") model { 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 743bbae21..9e7a4ff83 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,2 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.12") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.13") From af956da914072e45af5a6e230de6f1e4f90aaa11 Mon Sep 17 00:00:00 2001 From: Rich Dougherty Date: Tue, 29 May 2018 03:42:57 +1200 Subject: [PATCH 50/66] Updated with template-control on 2018-05-27T23:54:09.150Z (#63) **build.sbt: scalaVersion := "2.12.6" **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.15") **build.gradle: def playVersion = "2.6.15" --- build.gradle | 2 +- build.sbt | 2 +- project/plugins.sbt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 8863d25fa..6724479e5 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ plugins { id 'idea' } -def playVersion = "2.6.13" +def playVersion = "2.6.15" def scalaVersion = System.getProperty("scala.binary.version", /* default = */ "2.12") model { diff --git a/build.sbt b/build.sbt index fa139d159..63a45c8b5 100644 --- a/build.sbt +++ b/build.sbt @@ -4,7 +4,7 @@ version := "1.0-SNAPSHOT" lazy val root = (project in file(".")).enablePlugins(PlayScala) -scalaVersion := "2.12.4" +scalaVersion := "2.12.6" crossScalaVersions := Seq("2.11.12", "2.12.4") diff --git a/project/plugins.sbt b/project/plugins.sbt index 9e7a4ff83..adecc1828 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,2 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.13") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.15") From 58dc1fe6efd9b97a8b6d73857104f3bff77f94dd Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Fri, 8 Jun 2018 20:51:08 -0400 Subject: [PATCH 51/66] Upgrade branch 2.6.x using TemplateControl (#64) * Updated with template-control on 2018-06-08T21:15:48.498Z **/build.properties: sbt.version=1.1.6 * Add Java 10 and 11 to Travis build --- .travis.yml | 16 +++++++++++++++- gradle/wrapper/gradle-wrapper.properties | 2 +- project/build.properties | 2 +- scripts/script-helper | 8 ++++---- 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index a5b65ce36..9a6e92b7a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,10 +3,12 @@ scala: # When updating Scala versions, also check the excludes # in build matrix below. - 2.11.12 -- 2.12.4 +- 2.12.6 jdk: - oraclejdk8 - oraclejdk9 +- oraclejdk10 +- oraclejdk11 env: matrix: - SCRIPT=scripts/test-sbt @@ -28,6 +30,18 @@ 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 # See https://blog.travis-ci.com/2014-03-13-slack-notifications/ # created with travis encrypt command line tool diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 5ce78ed19..5a17e7455 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,4 @@ -distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-bin.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStorePath=wrapper/dists 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 diff --git a/scripts/script-helper b/scripts/script-helper index ac39d9a51..9a2faa643 100644 --- a/scripts/script-helper +++ b/scripts/script-helper @@ -5,9 +5,9 @@ set -o pipefail java_version=$(java -version 2>&1 | java -version 2>&1 | awk -F '"' '/version/ {print $2}') -if [[ $java_version = 9* ]] ; then - echo "The build is using Java 9 ($java_version). We need additional JVM parameters" - export _JAVA_OPTIONS="$_JAVA_OPTIONS --add-modules=java.xml.bind" +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 NOT using Java 9 ($java_version). No addional JVM params needed." + 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 From 9fb55863bce052d62c064fc446bf8ec47854519c Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Mon, 16 Jul 2018 17:55:13 -0400 Subject: [PATCH 52/66] Updated with template-control on 2018-07-16T18:38:45.033Z (#65) **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.16") **build.gradle: def playVersion = "2.6.16" --- build.gradle | 2 +- project/plugins.sbt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 6724479e5..e9a56c477 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ plugins { id 'idea' } -def playVersion = "2.6.15" +def playVersion = "2.6.16" def scalaVersion = System.getProperty("scala.binary.version", /* default = */ "2.12") model { diff --git a/project/plugins.sbt b/project/plugins.sbt index adecc1828..5a07ff4c1 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,2 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.15") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.16") From c9ed1b8987e7eb44463a382bebfc37e8b9bba519 Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Thu, 19 Jul 2018 12:24:58 -0400 Subject: [PATCH 53/66] Updated with template-control on 2018-07-19T01:58:55.403Z (#66) **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.17") **build.gradle: def playVersion = "2.6.17" --- build.gradle | 2 +- project/plugins.sbt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index e9a56c477..9417c5773 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ plugins { id 'idea' } -def playVersion = "2.6.16" +def playVersion = "2.6.17" def scalaVersion = System.getProperty("scala.binary.version", /* default = */ "2.12") model { diff --git a/project/plugins.sbt b/project/plugins.sbt index 5a07ff4c1..65e9b7203 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,2 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.16") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.17") From 885c1789f62565a02e5650afdee9d994fbb22d1d Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Mon, 20 Aug 2018 19:37:01 -0400 Subject: [PATCH 54/66] Updated with template-control on 2018-08-20T20:37:13.694Z (#68) **/build.properties: sbt.version=1.2.1 **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.18") **build.gradle: def playVersion = "2.6.18" **gradle/wrapper/gradle-wrapper.properties: distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- project/build.properties | 2 +- project/plugins.sbt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 9417c5773..b0565341a 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ plugins { id 'idea' } -def playVersion = "2.6.17" +def playVersion = "2.6.18" def scalaVersion = System.getProperty("scala.binary.version", /* default = */ "2.12") model { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 5a17e7455..89dba2d9d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,4 @@ -distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStorePath=wrapper/dists 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 65e9b7203..a6aaa8a6a 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,2 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.17") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.18") From 7d9ee26eb74761d50bd0647218600a8a055cd0f3 Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Tue, 11 Sep 2018 18:09:25 -0400 Subject: [PATCH 55/66] Updated with template-control on 2018-09-11T20:14:49.815Z (#69) **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.19") **build.gradle: def playVersion = "2.6.19" --- build.gradle | 2 +- project/plugins.sbt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index b0565341a..34622de2b 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ plugins { id 'idea' } -def playVersion = "2.6.18" +def playVersion = "2.6.19" def scalaVersion = System.getProperty("scala.binary.version", /* default = */ "2.12") model { diff --git a/project/plugins.sbt b/project/plugins.sbt index a6aaa8a6a..e73e70f26 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,2 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.18") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.19") From 9b28dc749af9fb914991bd96fc422d071e8f4753 Mon Sep 17 00:00:00 2001 From: Renato Cavalcanti Date: Mon, 8 Oct 2018 21:11:41 +0200 Subject: [PATCH 56/66] Updated with template-control on 2018-10-08T19:11:41.162Z **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.20") **build.gradle: def playVersion = "2.6.20" --- build.gradle | 2 +- project/plugins.sbt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 34622de2b..960878f16 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ plugins { id 'idea' } -def playVersion = "2.6.19" +def playVersion = "2.6.20" def scalaVersion = System.getProperty("scala.binary.version", /* default = */ "2.12") model { diff --git a/project/plugins.sbt b/project/plugins.sbt index e73e70f26..3dec70378 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,2 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.19") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.20") From 3c4cf4740855a7c680f03733c5df70d8263040a9 Mon Sep 17 00:00:00 2001 From: Nikolas Date: Thu, 22 Nov 2018 11:51:35 +0000 Subject: [PATCH 57/66] Made minor changes to the README file documentation --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 219f948ac..a2e870f1c 100644 --- a/README.md +++ b/README.md @@ -51,8 +51,8 @@ class ServerSpec extends PlaySpec ## Further Documentation -* -* -* -* -* +* [Compile Time Dependency Injection](https://www.playframework.com/documentation/latest/ScalaCompileTimeDependencyInjection) +* [Using ScalaTest + Play](https://www.playframework.com/documentation/latest/ScalaTestingWithScalaTest#Using-ScalaTest-+-Play) +* [ScalaTest User Guide](http://www.scalatest.org/user_guide) +* [ScalaTest/Scalactic 3.0.0 Release Notes](http://www.scalatest.org/release_notes/3.0.0) +* [ScalaTest Plus Play](https://github.com/playframework/scalatestplus-play) From f063cbf1a01ae122478f4b67ddd4f5bcb1f26747 Mon Sep 17 00:00:00 2001 From: Renato Cavalcanti Date: Thu, 29 Nov 2018 16:50:43 +0100 Subject: [PATCH 58/66] Updated with template-control on 2018-11-29T15:50:43.529Z /.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 63a45c8b5..91fedbabb 100644 --- a/build.sbt +++ b/build.sbt @@ -4,7 +4,7 @@ version := "1.0-SNAPSHOT" lazy val root = (project in file(".")).enablePlugins(PlayScala) -scalaVersion := "2.12.6" +scalaVersion := "2.12.7" crossScalaVersions := Seq("2.11.12", "2.12.4") From 1529c9d0fb960d43297894b874f08524097288c3 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 4 Dec 2018 08:22:03 +0000 Subject: [PATCH 59/66] Drop oraclejdk10 --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9a6e92b7a..2a9c6a66c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,6 @@ scala: jdk: - oraclejdk8 - oraclejdk9 -- oraclejdk10 - oraclejdk11 env: matrix: From 7f39234aa0da0f43f529d5e9329b83d50680fb64 Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Fri, 4 Jan 2019 13:34:24 -0500 Subject: [PATCH 60/66] Updated with template-control on 2019-01-04T17:13:23.804Z (#76) /.travis.yml: wrote /.travis.yml **build.sbt: scalaVersion := "2.12.8" **/build.properties: sbt.version=1.2.8 --- .travis.yml | 79 ++++++++++++++++++++-------------------- build.sbt | 2 +- project/build.properties | 2 +- 3 files changed, 42 insertions(+), 41 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2a9c6a66c..1e8c0e7c4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,49 +1,50 @@ language: scala scala: -# When updating Scala versions, also check the excludes -# in build matrix below. -- 2.11.12 -- 2.12.6 -jdk: -- oraclejdk8 -- oraclejdk9 -- oraclejdk11 + - 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: - - SCRIPT=scripts/test-sbt - - SCRIPT=scripts/test-gradle -script: -- $SCRIPT -cache: - directories: - - "$HOME/.ivy2/cache" - - "$HOME/.gradle/caches" -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 + # 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: - exclude: - - scala: 2.11.12 - jdk: oraclejdk9 - - scala: 2.11.12 - jdk: oraclejdk10 - - scala: 2.11.12 - jdk: oraclejdk11 + fast_finish: true 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 -# See https://blog.travis-ci.com/2014-03-13-slack-notifications/ -# created with travis encrypt command line tool -notifications: - slack: - secure: e2qJtRrHD8Mb9GhZXXeVzfxwZjRgKBq1FUv825gXB+iRm1Vn2J+Ysk3Hv7xrANxo2A6JXkPMtZG8wyzcYDcKrRX7CxMYbytlrqI0+7v8VicdO8wDerplvROmWoYpKOFmxza0wz3ENL8IEo2pkD8baPWYty9a+GEIGwuyKsuqGNEKzW7JHtWZXdKE8gEQKneNxErtFclI4yQqHk0NHJu+8/+BViNQ1PzaRAIEOj690x2ls9x2joEIQzSUeMdNBbjc5Xi45dDPgjb5jYwwjLRqChSOrEDOdF6cPpSYFXENizVd85LaoES3iL+1J8lG7HBARCvtASn+5KiXwVUtPCoiabqE4jmTkhi124PLU17fnhJSboPn5G8FrjoCYPhzSOFzWAkwAIsGBKbL4pC+sqh/HFbh/k/YwyksBZIrWpu0BfgVpCxqaf/1fM5Yj12yth1QLrDz5dOSoeJN2jovKyD9UAJlhq+SOBKpYBE3g6TTcRPFqNxY0gSZPCXP25XTxExWX1ZV76zAjh8gJcTpBzpqyOAyuylrfRfSnS8TrM81xeh49XnJMqSDHsDBnjiWKyAp4xtVG1Rch4akNcfBPf7LKL4Ve2JM7rRWijw4cIRBX1VpZ4SRwafdNLi4xIZimtVnye+nk9K1DgwmItls78mACWOKIzGsPf+DI6NALmU2M/8= +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 91fedbabb..3a56ee61e 100644 --- a/build.sbt +++ b/build.sbt @@ -4,7 +4,7 @@ version := "1.0-SNAPSHOT" lazy val root = (project in file(".")).enablePlugins(PlayScala) -scalaVersion := "2.12.7" +scalaVersion := "2.12.8" crossScalaVersions := Seq("2.11.12", "2.12.4") 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 From f1fbcf00d8277f79084f429a9faa28ca7501a963 Mon Sep 17 00:00:00 2001 From: Renato Cavalcanti Date: Tue, 8 Jan 2019 17:00:00 +0100 Subject: [PATCH 61/66] Updated with template-control on 2019-01-08T14:44:42.104Z (#81) **/plugins.sbt: addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.21") **build.gradle: def playVersion = "2.6.21" **build.gradle: playTest "org.scalatestplus.play:scalatestplus-play_\$scalaVersion:3.1.2" --- build.gradle | 4 ++-- project/plugins.sbt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 960878f16..ecbe127f2 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ plugins { id 'idea' } -def playVersion = "2.6.20" +def playVersion = "2.6.21" def scalaVersion = System.getProperty("scala.binary.version", /* default = */ "2.12") model { @@ -24,7 +24,7 @@ model { dependencies { play "com.typesafe.play:play-logback_$scalaVersion:$playVersion" play "com.typesafe.play:filters-helpers_$scalaVersion:$playVersion" - playTest "org.scalatestplus.play:scalatestplus-play_$scalaVersion:3.1.2" + playTest "org.scalatestplus.play:scalatestplus-play_\$scalaVersion:3.1.2" } repositories { diff --git a/project/plugins.sbt b/project/plugins.sbt index 3dec70378..372755e6f 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,2 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.20") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.21") From 5ad8c1f960477bb69af37874a43b4ed30cf2700b Mon Sep 17 00:00:00 2001 From: Renato Cavalcanti Date: Wed, 16 Jan 2019 13:08:45 +0100 Subject: [PATCH 62/66] Updated with template-control on 2019-01-16T12:08:45.884Z /LICENSE: wrote /LICENSE /NOTICE: wrote /NOTICE /.mergify.yml: wrote /.mergify.yml **build.gradle: playTest "org.scalatestplus.play:scalatestplus-play_$scalaVersion:3.1.2" --- .mergify.yml | 10 ++--- LICENSE | 119 ++++++++++++++++++++++++++++++++++++++++++++++++--- NOTICE | 8 ++++ build.gradle | 2 +- 4 files changed, 126 insertions(+), 13 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 . diff --git a/build.gradle b/build.gradle index ecbe127f2..87fa68d91 100644 --- a/build.gradle +++ b/build.gradle @@ -24,7 +24,7 @@ model { dependencies { play "com.typesafe.play:play-logback_$scalaVersion:$playVersion" play "com.typesafe.play:filters-helpers_$scalaVersion:$playVersion" - playTest "org.scalatestplus.play:scalatestplus-play_\$scalaVersion:3.1.2" + playTest "org.scalatestplus.play:scalatestplus-play_$scalaVersion:3.1.2" } repositories { From 2079a260841127582c7b1223c45e674d030f8e43 Mon Sep 17 00:00:00 2001 From: Renato Cavalcanti Date: Thu, 17 Jan 2019 16:10:58 +0100 Subject: [PATCH 63/66] Updated with template-control on 2019-01-17T15:10:58.005Z /.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 4d5c4b728e8752cf8b815f3447cffa2cecbd059b Mon Sep 17 00:00:00 2001 From: Renato Cavalcanti Date: Fri, 1 Feb 2019 11:40:33 +0100 Subject: [PATCH 64/66] Updated with template-control on 2019-02-01T10:40:33.389Z /.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 b775471e5d7a65924940edc1cb3e90baefa8b5c1 Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Thu, 14 Feb 2019 00:39:45 -0500 Subject: [PATCH 65/66] Updated with template-control on 2019-02-13T20:25:39.413Z (#87) /.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 0728b9b19257e83456872aeb9e4c93b1844fc0eb Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 23 Apr 2019 15:49:56 +0100 Subject: [PATCH 66/66] Nest play-scala-compile-di-example --- .../.gitignore | 0 .../.mergify.yml | 0 .../.travis.yml | 0 LICENSE => play-scala-compile-di-example/LICENSE | 0 NOTICE => play-scala-compile-di-example/NOTICE | 0 .../README.md | 0 .../app}/MyApplicationLoader.scala | 0 .../app}/controllers/HomeController.scala | 0 .../app}/views/index.scala.html | 0 .../app}/views/main.scala.html | 0 .../build.gradle | 0 .../build.sbt | 0 .../conf}/application.conf | 0 .../conf}/logback.xml | 0 {conf => play-scala-compile-di-example/conf}/routes | 0 .../gradle}/wrapper/gradle-wrapper.jar | Bin .../gradle}/wrapper/gradle-wrapper.properties | 0 gradlew => play-scala-compile-di-example/gradlew | 0 .../gradlew.bat | 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}/ApplicationSpec.scala | 0 .../test}/MyApplicationFactory.scala | 0 .../test}/SeleniumSpec.scala | 0 .../test}/ServerSpec.scala | 0 31 files changed, 0 insertions(+), 0 deletions(-) rename .gitignore => play-scala-compile-di-example/.gitignore (100%) rename .mergify.yml => play-scala-compile-di-example/.mergify.yml (100%) rename .travis.yml => play-scala-compile-di-example/.travis.yml (100%) rename LICENSE => play-scala-compile-di-example/LICENSE (100%) rename NOTICE => play-scala-compile-di-example/NOTICE (100%) rename README.md => play-scala-compile-di-example/README.md (100%) rename {app => play-scala-compile-di-example/app}/MyApplicationLoader.scala (100%) rename {app => play-scala-compile-di-example/app}/controllers/HomeController.scala (100%) rename {app => play-scala-compile-di-example/app}/views/index.scala.html (100%) rename {app => play-scala-compile-di-example/app}/views/main.scala.html (100%) rename build.gradle => play-scala-compile-di-example/build.gradle (100%) rename build.sbt => play-scala-compile-di-example/build.sbt (100%) rename {conf => play-scala-compile-di-example/conf}/application.conf (100%) rename {conf => play-scala-compile-di-example/conf}/logback.xml (100%) rename {conf => play-scala-compile-di-example/conf}/routes (100%) rename {gradle => play-scala-compile-di-example/gradle}/wrapper/gradle-wrapper.jar (100%) rename {gradle => play-scala-compile-di-example/gradle}/wrapper/gradle-wrapper.properties (100%) rename gradlew => play-scala-compile-di-example/gradlew (100%) rename gradlew.bat => play-scala-compile-di-example/gradlew.bat (100%) rename {project => play-scala-compile-di-example/project}/build.properties (100%) rename {project => play-scala-compile-di-example/project}/plugins.sbt (100%) rename {public => play-scala-compile-di-example/public}/images/favicon.png (100%) rename {public => play-scala-compile-di-example/public}/javascripts/hello.js (100%) rename {public => play-scala-compile-di-example/public}/stylesheets/main.css (100%) rename {scripts => play-scala-compile-di-example/scripts}/script-helper (100%) rename {scripts => play-scala-compile-di-example/scripts}/test-gradle (100%) rename {scripts => play-scala-compile-di-example/scripts}/test-sbt (100%) rename {test => play-scala-compile-di-example/test}/ApplicationSpec.scala (100%) rename {test => play-scala-compile-di-example/test}/MyApplicationFactory.scala (100%) rename {test => play-scala-compile-di-example/test}/SeleniumSpec.scala (100%) rename {test => play-scala-compile-di-example/test}/ServerSpec.scala (100%) diff --git a/.gitignore b/play-scala-compile-di-example/.gitignore similarity index 100% rename from .gitignore rename to play-scala-compile-di-example/.gitignore diff --git a/.mergify.yml b/play-scala-compile-di-example/.mergify.yml similarity index 100% rename from .mergify.yml rename to play-scala-compile-di-example/.mergify.yml diff --git a/.travis.yml b/play-scala-compile-di-example/.travis.yml similarity index 100% rename from .travis.yml rename to play-scala-compile-di-example/.travis.yml diff --git a/LICENSE b/play-scala-compile-di-example/LICENSE similarity index 100% rename from LICENSE rename to play-scala-compile-di-example/LICENSE diff --git a/NOTICE b/play-scala-compile-di-example/NOTICE similarity index 100% rename from NOTICE rename to play-scala-compile-di-example/NOTICE diff --git a/README.md b/play-scala-compile-di-example/README.md similarity index 100% rename from README.md rename to play-scala-compile-di-example/README.md diff --git a/app/MyApplicationLoader.scala b/play-scala-compile-di-example/app/MyApplicationLoader.scala similarity index 100% rename from app/MyApplicationLoader.scala rename to play-scala-compile-di-example/app/MyApplicationLoader.scala diff --git a/app/controllers/HomeController.scala b/play-scala-compile-di-example/app/controllers/HomeController.scala similarity index 100% rename from app/controllers/HomeController.scala rename to play-scala-compile-di-example/app/controllers/HomeController.scala diff --git a/app/views/index.scala.html b/play-scala-compile-di-example/app/views/index.scala.html similarity index 100% rename from app/views/index.scala.html rename to play-scala-compile-di-example/app/views/index.scala.html diff --git a/app/views/main.scala.html b/play-scala-compile-di-example/app/views/main.scala.html similarity index 100% rename from app/views/main.scala.html rename to play-scala-compile-di-example/app/views/main.scala.html diff --git a/build.gradle b/play-scala-compile-di-example/build.gradle similarity index 100% rename from build.gradle rename to play-scala-compile-di-example/build.gradle diff --git a/build.sbt b/play-scala-compile-di-example/build.sbt similarity index 100% rename from build.sbt rename to play-scala-compile-di-example/build.sbt diff --git a/conf/application.conf b/play-scala-compile-di-example/conf/application.conf similarity index 100% rename from conf/application.conf rename to play-scala-compile-di-example/conf/application.conf diff --git a/conf/logback.xml b/play-scala-compile-di-example/conf/logback.xml similarity index 100% rename from conf/logback.xml rename to play-scala-compile-di-example/conf/logback.xml diff --git a/conf/routes b/play-scala-compile-di-example/conf/routes similarity index 100% rename from conf/routes rename to play-scala-compile-di-example/conf/routes diff --git a/gradle/wrapper/gradle-wrapper.jar b/play-scala-compile-di-example/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from gradle/wrapper/gradle-wrapper.jar rename to play-scala-compile-di-example/gradle/wrapper/gradle-wrapper.jar diff --git a/gradle/wrapper/gradle-wrapper.properties b/play-scala-compile-di-example/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from gradle/wrapper/gradle-wrapper.properties rename to play-scala-compile-di-example/gradle/wrapper/gradle-wrapper.properties diff --git a/gradlew b/play-scala-compile-di-example/gradlew similarity index 100% rename from gradlew rename to play-scala-compile-di-example/gradlew diff --git a/gradlew.bat b/play-scala-compile-di-example/gradlew.bat similarity index 100% rename from gradlew.bat rename to play-scala-compile-di-example/gradlew.bat diff --git a/project/build.properties b/play-scala-compile-di-example/project/build.properties similarity index 100% rename from project/build.properties rename to play-scala-compile-di-example/project/build.properties diff --git a/project/plugins.sbt b/play-scala-compile-di-example/project/plugins.sbt similarity index 100% rename from project/plugins.sbt rename to play-scala-compile-di-example/project/plugins.sbt diff --git a/public/images/favicon.png b/play-scala-compile-di-example/public/images/favicon.png similarity index 100% rename from public/images/favicon.png rename to play-scala-compile-di-example/public/images/favicon.png diff --git a/public/javascripts/hello.js b/play-scala-compile-di-example/public/javascripts/hello.js similarity index 100% rename from public/javascripts/hello.js rename to play-scala-compile-di-example/public/javascripts/hello.js diff --git a/public/stylesheets/main.css b/play-scala-compile-di-example/public/stylesheets/main.css similarity index 100% rename from public/stylesheets/main.css rename to play-scala-compile-di-example/public/stylesheets/main.css diff --git a/scripts/script-helper b/play-scala-compile-di-example/scripts/script-helper similarity index 100% rename from scripts/script-helper rename to play-scala-compile-di-example/scripts/script-helper diff --git a/scripts/test-gradle b/play-scala-compile-di-example/scripts/test-gradle similarity index 100% rename from scripts/test-gradle rename to play-scala-compile-di-example/scripts/test-gradle diff --git a/scripts/test-sbt b/play-scala-compile-di-example/scripts/test-sbt similarity index 100% rename from scripts/test-sbt rename to play-scala-compile-di-example/scripts/test-sbt diff --git a/test/ApplicationSpec.scala b/play-scala-compile-di-example/test/ApplicationSpec.scala similarity index 100% rename from test/ApplicationSpec.scala rename to play-scala-compile-di-example/test/ApplicationSpec.scala diff --git a/test/MyApplicationFactory.scala b/play-scala-compile-di-example/test/MyApplicationFactory.scala similarity index 100% rename from test/MyApplicationFactory.scala rename to play-scala-compile-di-example/test/MyApplicationFactory.scala diff --git a/test/SeleniumSpec.scala b/play-scala-compile-di-example/test/SeleniumSpec.scala similarity index 100% rename from test/SeleniumSpec.scala rename to play-scala-compile-di-example/test/SeleniumSpec.scala diff --git a/test/ServerSpec.scala b/play-scala-compile-di-example/test/ServerSpec.scala similarity index 100% rename from test/ServerSpec.scala rename to play-scala-compile-di-example/test/ServerSpec.scala