Browse Source

Update play-slick and move to play2-scala-slick

- Move to play2-scala directory and generate config
- Update to Play 2.3
- Update to Scala 2.11
- Update to Slick 2.1
- Update other libraries
- Refactor and simplify code so it can be shared by Anorm
Rich Dougherty 11 năm trước cách đây
mục cha
commit
b1fe912bf2
26 tập tin đã thay đổi với 192 bổ sung255 xóa
  1. 0 1
      .travis.yml
  2. 1 0
      frameworks/Java/play2-java/generate_config.py
  3. 0 27
      frameworks/Scala/play-slick/README.md
  4. 0 0
      frameworks/Scala/play-slick/__init__.py
  5. 0 94
      frameworks/Scala/play-slick/app/controllers/Application.scala
  6. 0 20
      frameworks/Scala/play-slick/app/utils/PredicatedAction.scala
  7. 0 26
      frameworks/Scala/play-slick/benchmark_config
  8. 0 3
      frameworks/Scala/play-slick/install.sh
  9. 0 20
      frameworks/Scala/play-slick/project/Build.scala
  10. 0 1
      frameworks/Scala/play-slick/project/build.properties
  11. 0 52
      frameworks/Scala/play-slick/setup.py
  12. 21 0
      frameworks/Scala/play2-scala/benchmark_config
  13. 0 0
      frameworks/Scala/play2-scala/play2-scala-slick/.gitignore
  14. 91 0
      frameworks/Scala/play2-scala/play2-scala-slick/app/controllers/Application.scala
  15. 1 1
      frameworks/Scala/play2-scala/play2-scala-slick/app/models/Fortune.scala
  16. 6 7
      frameworks/Scala/play2-scala/play2-scala-slick/app/models/World.scala
  17. 47 0
      frameworks/Scala/play2-scala/play2-scala-slick/app/utils/DbOperation.scala
  18. 0 0
      frameworks/Scala/play2-scala/play2-scala-slick/app/views/fortune.scala.html
  19. 0 0
      frameworks/Scala/play2-scala/play2-scala-slick/app/views/main.scala.html
  20. 11 0
      frameworks/Scala/play2-scala/play2-scala-slick/build.sbt
  21. 3 0
      frameworks/Scala/play2-scala/play2-scala-slick/conf/application.conf
  22. 3 2
      frameworks/Scala/play2-scala/play2-scala-slick/conf/routes
  23. 1 0
      frameworks/Scala/play2-scala/play2-scala-slick/project/build.properties
  24. 1 1
      frameworks/Scala/play2-scala/play2-scala-slick/project/plugins.sbt
  25. 0 0
      frameworks/Scala/play2-scala/play2-scala-slick/source_code
  26. 6 0
      frameworks/Scala/play2-scala/setup_scala_slick.py

+ 0 - 1
.travis.yml

@@ -144,7 +144,6 @@ env:
     - "TESTDIR=Scala/plain"
     - "TESTDIR=Scala/play-activate-mysql"
     - "TESTDIR=Scala/play-scala-mongodb"
-    - "TESTDIR=Scala/play-slick"
     - "TESTDIR=Scala/play2-scala"
     - "TESTDIR=Scala/scalatra"
     - "TESTDIR=Scala/scruffy"

+ 1 - 0
frameworks/Java/play2-java/generate_config.py

@@ -13,6 +13,7 @@ configurations = [
   ('Java',  'Ebean', ['Linux'],            ['db', 'query']),
   ('Scala', None,    ['Linux'],            ['json']),
   ('Scala', 'Anorm', ['Linux', 'Windows'], ['db', 'query', 'fortune', 'update']),
+  ('Scala', 'Slick', ['Linux'],            ['db', 'query', 'fortune', 'update']),
 ]
 
 # All play2 test applications must use the same URLs.

+ 0 - 27
frameworks/Scala/play-slick/README.md

@@ -1,27 +0,0 @@
-#Play Benchmarking Test
-
-This is the Play portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
-
-### JSON Encoding Test
-
-* [JSON test source](app/controllers/Application.scala)
-
-### Data-Store/Database Mapping Test
-
-* [Database test controller](app/controllers/Application.scala)
-* [Database test model](app/models/World.scala)
-
-## Infrastructure Software Versions
-The tests were run with:
-
-* [Java OpenJDK 1.7.0_09](http://openjdk.java.net/)
-* [Play 2.1.0](http://http://www.playframework.com/)
-
-## Test URLs
-### JSON Encoding Test
-
-http://localhost/json
-
-### Data-Store/Database Mapping Test
-
-http://localhost/db?queries=5

+ 0 - 0
frameworks/Scala/play-slick/__init__.py


+ 0 - 94
frameworks/Scala/play-slick/app/controllers/Application.scala

@@ -1,94 +0,0 @@
-package controllers
-
-import play.api.Play.current
-import play.api.mvc._
-import play.api.libs.json.Json
-import java.util.concurrent._
-import scala.concurrent._
-import models.{Worlds, World, Fortunes, Fortune, WorldsTableQuery, FortunesTableQuery}
-import utils._
-import scala.concurrent.Future
-
-import play.api.libs.concurrent.Execution.Implicits._
-import play.core.NamedThreadFactory
-
-object Application extends Controller {
-
-  private val MaxQueriesPerRequest = 20
-  private val TestDatabaseRows = 10000
-
-  private val partitionCount = current.configuration.getInt("db.default.partitionCount").getOrElse(2)
-  private val maxConnections =
-    partitionCount * current.configuration.getInt("db.default.maxConnectionsPerPartition").getOrElse(5)
-  private val minConnections =
-    partitionCount * current.configuration.getInt("db.default.minConnectionsPerPartition").getOrElse(5)
-
-  private val tpe = new ThreadPoolExecutor(minConnections, maxConnections,
-    0L, TimeUnit.MILLISECONDS,
-    new LinkedBlockingQueue[Runnable](),
-    new NamedThreadFactory("dbEc"))
-  private val dbEc = ExecutionContext.fromExecutorService(tpe)
-
-  private val worldsTable = new WorldsTableQuery
-  private val fortunesTable = new FortunesTableQuery
-
-  // A predicate for checking our ability to service database requests is determined by ensuring that the request
-  // queue doesn't fill up beyond a certain threshold. For convenience we use the max number of connections * the max
-  // # of db requests per web request to determine this threshold. It is a rough check as we don't know how many
-  // queries we're going to make or what other threads are running in parallel etc. Nevertheless, the check is
-  // adequate in order to throttle the acceptance of requests to the size of the pool.
-  def isDbAvailable: Boolean = (tpe.getQueue.size() < maxConnections * MaxQueriesPerRequest)
-
-  def db(queries: Int) = PredicatedAction(isDbAvailable, ServiceUnavailable) {
-    Action.async {
-      val random = ThreadLocalRandom.current()
-
-      val _worlds = Future.sequence((for {
-        _ <- 1 to queries
-      } yield Future(worldsTable.findById(random.nextInt(TestDatabaseRows) + 1))(dbEc)
-        ).toList)
-
-      _worlds map {
-        w => Ok(Json.toJson(w))
-      }
-    }
-  }
-
-  def fortunes() = PredicatedAction(isDbAvailable, ServiceUnavailable) {
-    Action.async {
-      Future(fortunesTable.getAll())(dbEc).map { fs =>
-        val fortunes =  Fortune(0, "Additional fortune added at request time.") +: fs
-        Ok(views.html.fortune(fortunes))
-      }
-    }
-  }
-
-  def update(queries: Int) = PredicatedAction(isDbAvailable, ServiceUnavailable) {
-    Action.async {
-      val random = ThreadLocalRandom.current()
-
-      val boundsCheckedQueries = queries match {
-        case q if q > 500 => 500
-        case q if q <   1 => 1
-        case _ => queries
-      }
-
-      val worlds = Future.sequence((for {
-        _ <- 1 to boundsCheckedQueries
-      } yield Future {
-          for {
-            world <- worldsTable.findById(random.nextInt(TestDatabaseRows) + 1) 
-          } yield {
-            val updatedWorld = world.copy(randomNumber = random.nextInt(TestDatabaseRows) + 1)
-            worldsTable.updateRandom(updatedWorld)
-            updatedWorld
-          }
-        }(dbEc)
-      ).toList)
-
-      worlds.map {
-        w => Ok(Json.toJson(w)).withHeaders("Server" -> "Netty")
-      }
-    }
-  }
-}

+ 0 - 20
frameworks/Scala/play-slick/app/utils/PredicatedAction.scala

@@ -1,20 +0,0 @@
-package utils
-
-import play.api.mvc._
-import scala.concurrent.{Promise, Future}
-
-/**
- * A predicated action is one where a condition must be satisfied in order to proceed with the request. If the
- * condition is not satisfied then a supplied status result is yielded.
- */
-class PredicatedActionBuilder {
-  def apply[A](p: => Boolean, failed: => SimpleResult)(action: Action[A]): Action[A] = new Action[A] {
-    def apply(request: Request[A]): Future[SimpleResult] = {
-      if (p) action(request) else Promise.successful(failed).future
-    }
-
-    lazy val parser = action.parser
-  }
-}
-
-object PredicatedAction extends PredicatedActionBuilder

+ 0 - 26
frameworks/Scala/play-slick/benchmark_config

@@ -1,26 +0,0 @@
-{
-  "framework": "play-slick",
-  "tests": [{
-    "default": {
-      "setup_file": "setup",
-      "db_url": "/db",
-      "query_url": "/db?queries=",
-      "fortune_url": "/fortunes",
-      "update_url": "/update?queries=",
-      "port": 9000,
-      "approach": "Realistic",
-      "classification": "Fullstack",
-      "database": "MySQL",
-      "framework": "play2",
-      "language": "Scala",
-      "orm": "Raw",
-      "platform": "Netty",
-      "webserver": "None",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "play-scala-slick",
-      "notes": "",
-      "versus": "netty"
-    }
-  }]
-}

+ 0 - 3
frameworks/Scala/play-slick/install.sh

@@ -1,3 +0,0 @@
-#!/bin/bash
-
-fw_depends play2

+ 0 - 20
frameworks/Scala/play-slick/project/Build.scala

@@ -1,20 +0,0 @@
-import sbt._
-import Keys._
-import play.Project._
-
-object ApplicationBuild extends Build {
-
-  val appName         = "play-slick"
-  val appVersion      = "1.0-SNAPSHOT"
-
-  val appDependencies = Seq(
-    jdbc,
-    anorm,
-    "mysql" % "mysql-connector-java" % "5.1.22",
-    "com.typesafe.play" %% "play-slick" % "0.6.0.1"
-  )
-
-  val main = play.Project(appName, appVersion, appDependencies).settings(
-  )
-
-}

+ 0 - 1
frameworks/Scala/play-slick/project/build.properties

@@ -1 +0,0 @@
-sbt.version=0.13.0

+ 0 - 52
frameworks/Scala/play-slick/setup.py

@@ -1,52 +0,0 @@
-
-import subprocess
-import sys
-import setup_util
-import os
-from zipfile import ZipFile
-
-def start(args, logfile, errfile):
-  setup_util.replace_text("play-slick/conf/application.conf", "jdbc:mysql:\/\/.*:3306", "jdbc:mysql://" + args.database_host + ":3306")
-
-  subprocess.check_call("play clean dist", shell=True, cwd="play-slick", stderr=errfile, stdout=logfile)
-
-  if os.name == 'nt':
-    ZipFile("./play-slick/target/universal/play-slick-1.0-SNAPSHOT.zip").extractall("./play-slick/target/universal")
-    with open("./play-slick/target/universal/play-slick-1.0-SNAPSHOT/bin/play-slick.bat", "w+") as f:
-      f.write("java %1 -cp \"./lib/*;\" play.core.server.NettyServer .")
-    subprocess.Popen("play-slick.bat", shell=True, cwd="play-slick/target/universal/play-slick-1.0-SNAPSHOT/bin", stderr=errfile, stdout=logfile)
-  else:
-    subprocess.check_call("unzip play-slick-1.0-SNAPSHOT.zip", shell=True, cwd="play-slick/target/universal", stderr=errfile, stdout=logfile)
-    subprocess.check_call("chmod +x play-slick", shell=True, cwd="play-slick/target/universal/play-slick-1.0-SNAPSHOT/bin", stderr=errfile, stdout=logfile)
-    subprocess.Popen("./play-slick", shell=True, cwd="play-slick/target/universal/play-slick-1.0-SNAPSHOT/bin", stderr=errfile, stdout=logfile)
-
-  return 0
-def stop(logfile, errfile):
-  #if os.name == 'nt':
-  #  with open("./play-slick/target/universal/play-slick-1.0-SNAPSHOT/RUNNING_PID") as f:
-  #    pid = int(f.read())
-  #    os.kill(pid, 9)
-  #else:
-    #p = subprocess.Popen(['ps', 'ef'], stdout=subprocess.PIPE)
-    #out, err = p.communicate()
-    #for line in out.splitlines():
-    #  if 'NettyServer' in line:
-    #    pid = int(line.split(None, 2)[1])
-    #    os.kill(pid, 9)
-  with open("./play-slick/target/universal/play-slick-1.0-SNAPSHOT/RUNNING_PID") as f:
-    pid = int(f.read())
-    os.kill(pid, 15)
-
-  try:
-    #os.remove("play-slick/target/universal/play-slick-1.0-SNAPSHOT/RUNNING_PID")
-    os.remove("play-slick/target/universal/play-slick-1.0-SNAPSHOT/play-slick-1.0-SNAPSHOT/RUNNING_PID")
-  except OSError:
-    return 1
-
-  # Takes up so much disk space
-  if os.name == 'nt':
-    subprocess.check_call("del /f /s /q target", shell=True, cwd="play-slick", stderr=errfile, stdout=logfile)
-  else:
-    subprocess.check_call("rm -rf target", shell=True, cwd="play-slick", stderr=errfile, stdout=logfile)
-
-  return 0

+ 21 - 0
frameworks/Scala/play2-scala/benchmark_config

@@ -61,6 +61,27 @@
         "query_url": "/queries?queries=", 
         "fortune_url": "/fortunes", 
         "update_url": "/update?queries="
+      }, 
+      "scala-slick": {
+        "display_name": "play2-scala-slick", 
+        "setup_file": "setup_scala_slick", 
+        "framework": "play2", 
+        "language": "Scala", 
+        "orm": "Slick", 
+        "os": "Linux", 
+        "database": "MySQL", 
+        "approach": "Realistic", 
+        "classification": "Fullstack", 
+        "platform": "Netty", 
+        "webserver": "None", 
+        "database_os": "Linux", 
+        "notes": "", 
+        "versus": "netty", 
+        "port": "9000", 
+        "db_url": "/db", 
+        "query_url": "/queries?queries=", 
+        "fortune_url": "/fortunes", 
+        "update_url": "/update?queries="
       }
     }
   ]

+ 0 - 0
frameworks/Scala/play-slick/.gitignore → frameworks/Scala/play2-scala/play2-scala-slick/.gitignore


+ 91 - 0
frameworks/Scala/play2-scala/play2-scala-slick/app/controllers/Application.scala

@@ -0,0 +1,91 @@
+package controllers
+
+import java.util.concurrent._
+import play.api.Play.current
+import play.api.mvc._
+import play.api.libs.json.Json
+import scala.concurrent._
+import models.{Worlds, World, Fortunes, Fortune, WorldsTableQuery, FortunesTableQuery}
+import utils._
+import utils.DbOperation._
+import scala.concurrent.Future
+import scala.slick.jdbc.JdbcBackend.Session
+
+import play.api.libs.concurrent.Execution.Implicits._
+
+object Application extends Controller {
+
+  // Slick code
+
+  private val worldsTable = new WorldsTableQuery
+  private val fortunesTable = new FortunesTableQuery
+
+  def getRandomWorlds(n: Int): Future[Seq[World]] = asyncDbOp { implicit session =>
+    val random = ThreadLocalRandom.current()
+    for (_ <- 1 to n) yield {
+      val randomId = random.nextInt(TestDatabaseRows) + 1
+      worldsTable.findById(randomId)
+    }
+  }
+
+  def getFortunes(): Future[Seq[Fortune]] = asyncDbOp { implicit session =>
+    fortunesTable.getAll()
+  }
+
+  def updateWorlds(n: Int): Future[Seq[World]] = asyncDbOp { implicit session =>
+    val random = ThreadLocalRandom.current()
+    for (_ <- 1 to n) yield {
+      val randomId = random.nextInt(TestDatabaseRows) + 1
+      val world = worldsTable.findById(randomId)
+      val randomNumber = random.nextInt(10000) + 1
+      val updatedWorld = world.copy(randomNumber = randomNumber)
+      worldsTable.updateRandom(updatedWorld)
+      updatedWorld
+    }
+  }
+
+  // Common code between Anorm and Slick
+
+  protected val TestDatabaseRows = 10000
+
+  def db = Action.async {
+    getRandomWorlds(1).map { worlds =>
+      Ok(Json.toJson(worlds.head))
+    }
+  }
+
+  def queries(countString: String) = Action.async {
+    val n = parseCount(countString)
+    getRandomWorlds(n).map { worlds =>
+      Ok(Json.toJson(worlds))
+    }
+  }
+
+  def fortunes() = Action.async {
+    getFortunes().map { dbFortunes =>
+      val appendedFortunes =  Fortune(0, "Additional fortune added at request time.") :: (dbFortunes.to[List])
+      Ok(views.html.fortune(appendedFortunes))
+    }
+  }
+
+  def update(queries: String) = Action.async {
+    val n = parseCount(queries)
+    updateWorlds(n).map { worlds =>
+      Ok(Json.toJson(worlds))
+    }
+  }
+
+  private def parseCount(s: String): Int = {
+    try {
+      val parsed = java.lang.Integer.parseInt(s, 10)
+      parsed match {
+        case i if i < 1 => 1
+        case i if i > 500 => 500
+        case i => i
+      }
+    } catch {
+      case _: NumberFormatException => 1
+    }
+  }
+
+}

+ 1 - 1
frameworks/Scala/play-slick/app/models/Fortune.scala → frameworks/Scala/play2-scala/play2-scala-slick/app/models/Fortune.scala

@@ -12,7 +12,7 @@ class Fortunes(tag: Tag) extends Table[Fortune](tag, "Fortune") {
 }
 class FortunesTableQuery extends TableQuery(new Fortunes(_)){
   val byId = this.findBy(_.id)
-  val all = Compiled{this:Query[Fortunes,Fortune]}
+  val all = Compiled{this:Query[Fortunes,Fortune,Seq]}
   def getAll(): List[Fortune] = DB.withSession { implicit session =>
     all.list
   }

+ 6 - 7
frameworks/Scala/play-slick/app/models/World.scala → frameworks/Scala/play2-scala/play2-scala-slick/app/models/World.scala

@@ -14,15 +14,14 @@ class Worlds(tag: Tag) extends Table[World](tag, "World") {
 class WorldsTableQuery extends TableQuery(new Worlds(_)){
   val byId = this.findBy(_.id)
 
-  def findById(id: Int): Option[World] = DB.withSession { implicit session =>
-      byId(id).firstOption
+  def findById(id: Int)(implicit session: Session): World = {
+    byId(id).first
   }
 
-  val updateQuery = Compiled{ (id: Column[Int]) => this.where(_.id === id) }
-  def updateRandom(world: World) {
-    DB.withSession { implicit session: Session =>
-      updateQuery(world.id).update(world)
-    }
+  val updateQuery = Compiled{ (id: Column[Int]) => this.filter(_.id === id) }
+
+  def updateRandom(world: World)(implicit session: Session) {
+    updateQuery(world.id).update(world)
   }  
 }
 

+ 47 - 0
frameworks/Scala/play2-scala/play2-scala-slick/app/utils/DbOperation.scala

@@ -0,0 +1,47 @@
+package utils
+
+import java.util.concurrent._
+import play.api.db.slick.DB
+import play.api.Play.current
+import play.core.NamedThreadFactory
+import scala.concurrent._
+import scala.concurrent.Future
+import scala.slick.jdbc.JdbcBackend.Session
+
+object DbOperation {
+
+  // Common code between Anorm and Slick
+
+  private val maxDbOperations = current.configuration.underlying.getInt("max-db-ops")
+
+  private val partitionCount = current.configuration.getInt("db.default.partitionCount").getOrElse(2)
+  private val maxConnections =
+    partitionCount * current.configuration.getInt("db.default.maxConnectionsPerPartition").getOrElse(5)
+  private val minConnections =
+    partitionCount * current.configuration.getInt("db.default.minConnectionsPerPartition").getOrElse(5)
+
+  private val tpe = new ThreadPoolExecutor(minConnections, maxConnections,
+    0L, TimeUnit.MILLISECONDS,
+    new LinkedBlockingQueue[Runnable](), // TODO: Could use ArrayBlockingQueue?
+    new NamedThreadFactory("dbEc"))
+  private val dbEc = ExecutionContext.fromExecutorService(tpe)
+
+  // Slick code
+
+  /**
+   * Run a DB operation in the DB context. Automatically
+   * provides a Session.
+   */
+  def asyncDbOp[T](op: Session => T): Future[T] = {
+    // If the thread-pool queue used by the database grows too large then our server
+    // is probably struggling, and we should start dropping requests. If we don't
+    // then we'll just slow everything down and it will fail anyway. Better to fail
+    // quickly rather than slowly. Set the max size of our queue something above the
+    // number of concurrent connections that we expect to be handling.
+    if (tpe.getQueue.size > maxDbOperations) sys.error(s"Aborted DB operation because queue is > $maxDbOperations")
+    Future {
+      DB.withSession { session => op(session) }
+    }(dbEc)
+  }
+
+}

+ 0 - 0
frameworks/Scala/play-slick/app/views/fortune.scala.html → frameworks/Scala/play2-scala/play2-scala-slick/app/views/fortune.scala.html


+ 0 - 0
frameworks/Scala/play-slick/app/views/main.scala.html → frameworks/Scala/play2-scala/play2-scala-slick/app/views/main.scala.html


+ 11 - 0
frameworks/Scala/play2-scala/play2-scala-slick/build.sbt

@@ -0,0 +1,11 @@
+name := "play2-scala-slick"
+
+version := "1.0-SNAPSHOT"
+
+scalaVersion := "2.11.2"
+
+lazy val root = (project in file(".")).enablePlugins(PlayScala)
+
+libraryDependencies ++= Seq(
+  "com.typesafe.play" %% "play-slick" % "0.8.0",
+  "mysql" % "mysql-connector-java" % "5.1.32")

+ 3 - 0
frameworks/Scala/play-slick/conf/application.conf → frameworks/Scala/play2-scala/play2-scala-slick/conf/application.conf

@@ -1,6 +1,9 @@
 # This is the main configuration file for the application.
 # ~~~~~
 
+# The max size of the queue for running db operations
+max-db-ops = 1024
+
 # Secret key
 # ~~~~~
 # The secret key is used to secure cryptographics functions.

+ 3 - 2
frameworks/Scala/play-slick/conf/routes → frameworks/Scala/play2-scala/play2-scala-slick/conf/routes

@@ -3,9 +3,10 @@
 # ~~~~
 
 # Home page
-GET     /db                             controllers.Application.db(queries: Int ?= 1)
+GET     /db                             controllers.Application.db
+GET     /queries                        controllers.Application.queries(queries ?= "1")
 GET     /fortunes                       controllers.Application.fortunes
-GET     /update                         controllers.Application.update(queries: Int ?= 1)
+GET     /update                         controllers.Application.update(queries ?= "1")
 
 # Map static resources from the /public folder to the /assets URL path
 GET     /assets/*file                   controllers.Assets.at(path="/public", file)

+ 1 - 0
frameworks/Scala/play2-scala/play2-scala-slick/project/build.properties

@@ -0,0 +1 @@
+sbt.version=0.13.5

+ 1 - 1
frameworks/Scala/play-slick/project/plugins.sbt → frameworks/Scala/play2-scala/play2-scala-slick/project/plugins.sbt

@@ -5,4 +5,4 @@ logLevel := Level.Warn
 resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/"
 
 // Use the Play sbt plugin for Play projects
-addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.2.0")
+addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.3")

+ 0 - 0
frameworks/Scala/play-slick/source_code → frameworks/Scala/play2-scala/play2-scala-slick/source_code


+ 6 - 0
frameworks/Scala/play2-scala/setup_scala_slick.py

@@ -0,0 +1,6 @@
+
+# This file was generated by frameworks/Java/play2-java/generate_config.py.
+# Do not edit this file directly, use the script to regenerate.
+from .setup_common import make_setup_for_dir
+
+make_setup_for_dir(globals(), 'play2-scala-slick')