Ответ 1
Я собираюсь не согласиться с Jens здесь, хотя и не так много.
Макет проекта
Мое собственное предложение состоит в том, что вы моделируете свои усилия на стандартном расположении каталога Maven.
Предыдущие версии SBT (до SBT 0.9.x) создавали бы это автоматически для вас:
[email protected]:~$ mkdir myproject
[email protected]:~$ cd myproject
[email protected]:~/myproject$ sbt
Project does not exist, create new project? (y/N/s) y
Name: myproject
Organization: org.dcsobral
Version [1.0]:
Scala version [2.7.7]: 2.8.1
sbt version [0.7.4]:
Getting Scala 2.7.7 ...
:: retrieving :: org.scala-tools.sbt#boot-scala
confs: [default]
2 artifacts copied, 0 already retrieved (9911kB/134ms)
Getting org.scala-tools.sbt sbt_2.7.7 0.7.4 ...
:: retrieving :: org.scala-tools.sbt#boot-app
confs: [default]
15 artifacts copied, 0 already retrieved (4096kB/91ms)
[success] Successfully initialized directory structure.
Getting Scala 2.8.1 ...
:: retrieving :: org.scala-tools.sbt#boot-scala
confs: [default]
2 artifacts copied, 0 already retrieved (15118kB/160ms)
[info] Building project myproject 1.0 against Scala 2.8.1
[info] using sbt.DefaultProject with sbt 0.7.4 and Scala 2.7.7
> quit
[info]
[info] Total session time: 8 s, completed May 6, 2011 12:31:43 PM
[success] Build completed successfully.
[email protected]:~/myproject$ find . -type d -print
.
./project
./project/boot
./project/boot/scala-2.7.7
./project/boot/scala-2.7.7/lib
./project/boot/scala-2.7.7/org.scala-tools.sbt
./project/boot/scala-2.7.7/org.scala-tools.sbt/sbt
./project/boot/scala-2.7.7/org.scala-tools.sbt/sbt/0.7.4
./project/boot/scala-2.7.7/org.scala-tools.sbt/sbt/0.7.4/compiler-interface-bin_2.7.7.final
./project/boot/scala-2.7.7/org.scala-tools.sbt/sbt/0.7.4/compiler-interface-src
./project/boot/scala-2.7.7/org.scala-tools.sbt/sbt/0.7.4/compiler-interface-bin_2.8.0.RC2
./project/boot/scala-2.7.7/org.scala-tools.sbt/sbt/0.7.4/xsbti
./project/boot/scala-2.8.1
./project/boot/scala-2.8.1/lib
./target
./lib
./src
./src/main
./src/main/resources
./src/main/scala
./src/test
./src/test/resources
./src/test/scala
Таким образом, вы будете размещать исходные файлы внутри myproject/src/main/scala
для основной программы или myproject/src/test/scala
для тестов.
Поскольку это больше не работает, есть несколько альтернатив:
giter8 и sbt.g8
Установите giter8, clone ymasory sbt.g8 и адаптируйте его к вашим потребностям и используйте это. См. Ниже, например, использование немодифицированного шаблона ymasory sbt.g8. Я думаю, что это одна из лучших альтернатив для запуска новых проектов, когда у вас есть хорошее представление о том, что вы хотите во всех своих проектах.
$ g8 ymasory/sbt
project_license_url [http://www.gnu.org/licenses/gpl-3.0.txt]:
name [myproj]:
project_group_id [com.example]:
developer_email [[email protected]]:
developer_full_name [John Doe]:
project_license_name [GPLv3]:
github_username [johndoe]:
Template applied in ./myproj
$ tree myproj
myproj
├── build.sbt
├── LICENSE
├── project
│ ├── build.properties
│ ├── build.scala
│ └── plugins.sbt
├── README.md
├── sbt
└── src
└── main
└── scala
└── Main.scala
4 directories, 8 files
np plugin
Использовать softprops np plugin для sbt. В приведенном ниже примере плагин настроен на ~/.sbt/plugins/build.sbt
и его настройках на ~/.sbt/np.sbt
со стандартным sbt script. Если вы используете paulp sbt-extras, вам нужно установить эти вещи под нужным подкаталогом Scala в ~/.sbt
, так как он использует отдельные конфигурации для каждой версии Scala. На практике это тот, который я использую чаще всего.
$ mkdir myproj; cd myproj
$ sbt 'np name:myproj org:com.example'
[info] Loading global plugins from /home/dcsobral/.sbt/plugins
[warn] Multiple resolvers having different access mechanism configured with same name 'sbt-plugin-releases'. To avoid conflict, Remove duplicate project resolvers (`resolvers`) or rename publishing resolver (`publishTo`).
[info] Set current project to default-c642a2 (in build file:/home/dcsobral/myproj/)
[info] Generated build file
[info] Generated source directories
[success] Total time: 0 s, completed Apr 12, 2013 12:08:31 PM
$ tree
.
├── build.sbt
├── src
│ ├── main
│ │ ├── resources
│ │ └── scala
│ └── test
│ ├── resources
│ └── scala
└── target
└── streams
└── compile
└── np
└── $global
└── out
12 directories, 2 files
MkDir
Вы можете просто создать его с помощью mkdir
:
$ mkdir -p myproj/src/{main,test}/{resource,scala,java}
$ tree myproj
myproj
└── src
├── main
│ ├── java
│ ├── resource
│ └── scala
└── test
├── java
├── resource
└── scala
9 directories, 0 files
Макет источника
Теперь об исходном макете. Йенс рекомендует использовать стиль Java. Ну, макет каталога Java - это требование - на Java. Scala не имеет того же требования, поэтому у вас есть возможность его выполнить или нет.
Если вы это сделаете, если базовый пакет org.dcsobral.myproject
, то исходный код для этого пакета будет помещен внутри myproject/src/main/scala/org/dcsobral/myproject/
и т.д. для подпакетов.
Два общих способа отклонения от этого стандарта:
-
Опускание каталога базового пакета и создание только подкаталогов для подпакетов.
Например, допустим, что у меня есть пакеты
org.dcsobral.myproject.model
,org.dcsobral.myproject.view
иorg.dcsobral.myproject.controller
, тогда каталоги будутmyproject/src/main/scala/model
,myproject/src/main/scala/view
иmyproject/src/main/scala/controller
. -
Собираем все вместе. В этом случае все исходные файлы будут находиться внутри
myproject/src/main/scala
. Это достаточно хорошо для небольших проектов. На самом деле, если у вас нет подпроектов, это то же самое, что и выше.
И это касается макета каталога.
Имена файлов
Затем поговорим о файлах. В Java практика разделяет каждый класс в собственном файле, имя которого будет следовать за именем класса. Это также достаточно хорошо в Scala, но вы должны обратить внимание на некоторые исключения.
Во-первых, Scala имеет object
, которого у Java нет. A class
и object
с тем же именем считаются компаньонами, которые имеют некоторые практические последствия, но только в том случае, если они находятся в одном файле. Итак, поместите сопутствующие классы и объекты в один и тот же файл.
Во-вторых, Scala имеет концепцию, известную как sealed class
(или trait
), которая ограничивает подклассы (или реализацию object
s) теми, кто указан в том же файле. В основном это делается для создания алгебраических типов данных с сопоставлением шаблонов с проверкой полноты. Например:
sealed abstract class Tree
case class Node(left: Tree, right: Tree) extends Tree
case class Leaf(n: Int) extends Tree
scala> def isLeaf(t: Tree) = t match {
| case Leaf(n: Int) => println("Leaf "+n)
| }
<console>:11: warning: match is not exhaustive!
missing combination Node
def isLeaf(t: Tree) = t match {
^
isLeaf: (t: Tree)Unit
Если Tree
не был sealed
, то любой мог бы расширить его, что не позволяет компилятору узнать, является ли совпадение исчерпывающим или нет. В любом случае, классы sealed
объединяются в один и тот же файл.
Другим соглашением об именах является имя файлов, содержащих package object
(для этого пакета) package.scala
.
Импорт материалов
Самое основное правило: вещи в одном пакете видят друг друга. Итак, поместите все в один и тот же пакет, и вам не нужно беспокоиться о том, что видит что.
Но Scala также имеют относительные ссылки и импорт. Это требует немного объяснений. Скажем, у меня есть следующие объявления в верхней части моего файла:
package org.dcsobral.myproject
package model
Все последующие будут помещены в пакет org.dcsobral.myproject.model
. Кроме того, будет видно не только все внутри этого пакета, но и все внутри org.dcsobral.myproject
. Если я просто объявил package org.dcsobral.myproject.model
вместо этого, то org.dcsobral.myproject
не будет виден.
Правило довольно простое, но вначале это может смутить людей. Причиной этого правила является относительный импорт. Рассмотрим теперь следующий оператор в этом файле:
import view._
Этот импорт может быть относительным - все импорт может быть относительным, если вы не префикс его с помощью _root_.
. Он может ссылаться на следующие пакеты: org.dcsobral.myproject.model.view
, org.dcsobral.myproject.view
, scala.view
и java.lang.view
. Он также может ссылаться на объект с именем view
внутри scala.Predef
. Или это может быть абсолютный импорт, относящийся к пакету с именем view
.
Если существует более одного такого пакета, он будет выбирать один в соответствии с некоторыми правилами приоритета. Если вам нужно импортировать что-то еще, вы можете превратить импорт в абсолютный.
Этот импорт делает все внутри пакета view
(где бы он ни был) видимым в своей области. Если это происходит внутри class
и object
или def
, тогда видимость будет ограничена этим. Он импортирует все из-за ._
, который является подстановочным знаком.
Альтернатива может выглядеть так:
package org.dcsobral.myproject.model
import org.dcsobral.myproject.view
import org.dcsobral.myproject.controller
В этом случае пакеты view
и controller
будут видны, но вы должны будете их явно указывать при их использовании:
def post(view: view.User): Node =
Или вы можете использовать дополнительный относительный импорт:
import view.User
Оператор import
также позволяет вам переименовывать материал или импортировать все, кроме чего-то. Более подробную информацию см. В соответствующей документации.
Итак, я надеюсь, что это ответит на все ваши вопросы.