Соглашения не всегда хороши. Недавно я начал новый внутренний веб-проект в 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 :
и вот как это выглядит при расширении:
Хотя мне потребовалось некоторое время, чтобы понять все это, я рад возможности структурировать свой проект так, как он имеет для нас смысл.
Ссылка: | Изменение каталогов Scalatra и sbt по умолчанию от нашего партнера по JCG Сандро Манкузо в блоге Crafted Software . |