Статьи

Изменение каталогов Scalatra и sbt по умолчанию

Соглашения не всегда хороши. Недавно я начал новый внутренний веб-проект в Codurance, и я решил использовать Scala. Поскольку я не фанат больших фреймворков, я выбрал Scalatra в качестве микро-фреймворка.

Первая проблема заключалась в том, что я хотел организовать свое приложение с другой структурой каталогов. По умолчанию sbt и Scalatra используют то же соглашение, что и maven :

1
2
3
4
> /src/main/scala       // source code
> /src/main/resources   // production resources
> /scr/test/scala       // tests
> /scr/test/resouces    // test resources

Последние несколько лет я экспериментировал с различной структурой каталогов для своих проектов. Я хочу, чтобы мои каталоги были более значимыми, когда речь идет о бизнес-сфере. Новая структура каталогов является частью того, что я называю интерактивным дизайном (IDD), и подробное описание этого можно найти в нашей области видео . В этом выступлении я подробно расскажу о рациональной структуре новой директории.

Структура каталогов, которую я хотел бы использовать для этого нового проекта:

01
02
03
04
05
06
07
08
09
10
> /src/core/scala          // source code for my core domain
> /src/core-test/scala     // tests for my core domain
 
> /src/data/resources      // resources for data migration and test data
> /src/data/scala          // code for data migration and test data
 
> /src/web/resources       // delivery mechanism resources
> /src/web/scala           // delivery mechanism code (controllers, API, etc)
> /src/web/webapp          // web files (WEB-INF folder, css, javascript, Jade templates, etc)
> /src/web-test/scala      // tests for my delivery mechanism

Еще раз, приведенная выше структура каталогов будет иметь больше смысла, если вы посмотрите выступление по интерактивному проектированию (IDD) .

Самой большой проблемой было переименование каталога по умолчанию main в web . Это сломало весь мир. Вот изменения, которые я должен был сделать, чтобы все это исправить:

build.sbt

01
02
03
04
05
06
07
08
09
10
11
12
unmanagedSourceDirectories in Compile := Seq((baseDirectory.value / "src/core/scala"),
                                             (baseDirectory.value / "src/data/scala"),
                                             (baseDirectory.value / "src/web/scala"))
 
unmanagedResourceDirectories in Compile += baseDirectory.value / "src/data/resources"
 
unmanagedSourceDirectories in Test := Seq((baseDirectory.value / "src/core-test/scala"),
                                          (baseDirectory.value / "src/web-test/scala"))
 
webappSrc in webapp <<= (baseDirectory in Compile) map { _ / "src/web/webapp" }
 
webappDest in webapp <<= (baseDirectory in Compile) map { _ / "src/web/webapp" }

Последние две строки webappSrc и webappDest были необходимы, потому что я также использую класс, который запускает Jetty вручную, где я подключаю слушателя Scalatra.

JettyLauncher.scala

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import org.eclipse.jetty.server.Server
import org.eclipse.jetty.servlet.DefaultServlet
import org.eclipse.jetty.webapp.WebAppContext
import org.scalatra.servlet.ScalatraListener
 
object JettyLauncher {
 
    def main(args: Array[String]) {
        val port = if(System.getenv("PORT") != null) System.getenv("PORT").toInt else 8089
 
        val server = new Server(port)
        val context = new WebAppContext()
        context.setClassLoader(JettyLauncher.getClass.getClassLoader)
        context setContextPath "/"
        context.setResourceBase("src/web/webapp")
        context.addEventListener(new ScalatraListener)
        context.addServlet(classOf[DefaultServlet], "/")
 
        server.setHandler(context)
 
        server.start
        server.join
    }
}

При выполнении этого класса ScalatraBootstrap не может быть найден, и поэтому мне пришлось добавить следующую строку в мой JettyLauncher :

1
> context.setClassLoader(JettyLauncher.getClass.getClassLoader)

Scalatra полагается на основную директорию по умолчанию для поиска ScalatraBootstrap, и именно так мне удалось убедиться, что ScalatraBootstrap может быть найден.

Обратите внимание, что мне также пришлось изменить базу ресурсов, указав на веб- папку вместо основной :

1
> context.setResourceBase("src/web/webapp")

Поскольку я использую шаблоны Jade через Scalate , мне пришлось изменить конфигурацию шаблона Scalate на build.scala .

build.scala

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
object MonitorBuild extends Build {
    val Organization = "com.codurance"
    val Name = "monitor"
    val Version = "0.1.0-SNAPSHOT"
    val ScalaVersion = "2.11.6"
    val ScalatraVersion = "2.4.0.RC1"
 
    lazy val project = Project(
        "monitor",
        file("."),
        settings = ScalatraPlugin.scalatraSettings ++ scalateSettings ++ Seq(
 
            // dependencies and some other stuff here
 
            scalateTemplateConfig in Compile <<= (sourceDirectory in Compile) { base =>
                Seq(
                    TemplateConfig(
                        new RichFile(new File("src")) / "web" / "webapp" / "WEB-INF" / "templates",
                        Seq.empty,
                        Seq(
                            Binding("context", "_root_.org.scalatra.scalate.ScalatraRenderContext", importMembers = true, isImplicit = true)
                        ),
                        Some("templates")
                    )
                )
            }
        )
    )
}

Важная строка выше:

1
> new RichFile(new File("src")) / "web" / "webapp" / "WEB-INF" / "templates"

Что заставляет Scalate находить шаблоны в веб- каталоге, а не в главном .

Убедитесь, что у вас есть эти строки в plugins.sbt

1
2
3
> addSbtPlugin("com.mojolly.scalate" % "xsbt-scalate-generator" % "0.5.0")
>
> addSbtPlugin("org.scalatra.sbt" % "scalatra-sbt" % "0.4.0")

Мне не нужно было ничего менять в моем web.xml и ScalatraBootstrap .

Описанная выше конфигурация позволяет мне запускать приложение через

1
> ./sbt container:start

это то, как я обычно запускаю приложение локально, а также позволяет мне создать толстый файл jar и выполнить класс JettyLauncher , как я запускаю в производственной среде :

1
> java -cp <myapplication>.jar com.codurance.JettyLauncher

Файл fat jar создается с помощью:

1
> ./sbt assembly

Вот как выглядит свернутая структура каталогов на IntelliJ IDEA :

2015-11-21 папка-структура-разрушилась

и вот как это выглядит при расширении:

2015-11-21 папка-структура вспененных

Хотя мне потребовалось некоторое время, чтобы понять все это, я рад возможности структурировать свой проект так, как он имеет для нас смысл.

Ссылка: Изменение каталогов Scalatra и sbt по умолчанию от нашего партнера по JCG Сандро Манкузо в блоге Crafted Software .