Play Framework — все, что вы хотели о нем узнать, но почему-то боялись спросить

12 ноября 2014

Play Framework — это MVC веб-фреймворк для языков программирования Java и Scala от компании Typesafe. С одной стороны, Play обладает гибкостью и простотой в использовании фреймворков типа Django или Mojolicious, с другой — в нем реализованы многие идеи, например, компилируемость, а следовательно и высокая производительность, строгая статическая типизация и тд, которые мы можем наблюдать, скажем, в Yesod.

Так получилось, что в мире Scala многие вещи очень быстро меняются. Поэтому, если вы хотите получить наиболее актуальную информацию по Play Framework, вам лучше обратиться к официальной документации от Typesafe. Тем не менее, большинство общих моментов остаются неизменными. Поэтому давайте попытаемся разобраться, как вообще выглядит проект на Play Framework.

На момент написания этих строк создание нового проекта на Play происходило при помощи утилиты Typesafe Activator как-то так:

activator new my-first-app play-scala

Полученная при этом структура проекта:

.
+-- activator
+-- activator.bat
+-- activator-launch-1.2.10.jar
+-- app
|   +-- controllers
|   |   +-- Application.scala
|   +-- views
|       +-- index.scala.html
|       +-- main.scala.html
+-- build.sbt
+-- conf
|   +-- application.conf
|   +-- routes
+-- LICENSE
+-- project
|   +-- build.properties
|   +-- plugins.sbt
+-- public
|   +-- images
|   |   +-- favicon.png
|   +-- javascripts
|   |   +-- hello.js
|   +-- stylesheets
|       +-- main.css
+-- README
+-- test
    +-- ApplicationSpec.scala
    +-- IntegrationSpec.scala

 

Тут следует обратить внимание на следующее:

  • activator — скрипт для запуска, тестирования, сборки проекта;
  • app/controllers/ — контроллеры, штуки, связывающие model и view;
  • app/views/ — html-, rss-, xml- и прочие шаблоны;
  • app/models/ — сюда обычно складывают классы для доступа к БД;
  • build.sbtтот самый build.sbt;
  • conf/application.conf — настройки приложения;
  • conf/routes — роуты, о них речь пойдет ниже;
  • public/ — всякая статика;
  • test/ — модульные и интеграционные тесты;

Для компиляции и запуска проекта говорим:

./activator run

По умолчанию приложение слушает порт 9000. При изменении кода проекта перекомпиляция будет происходить автоматически.

Еще можно прогнать тесты:

./activator test

А еще есть совершенно сумасшедшая штука:

./activator ui

… которая запускает на порту 8888 веб-интерфейс, через который можно собирать, запускать или тестировать проект, править код в редакторе с подсветкой синтаксиса и так далее. В действительности это самая настоящая IDE с веб-мордой!

Итак, допустим, мы хотим добавить в наше приложение новую страничку. Как это сделать? Для начала необходимо создать новый роут в файле conf/routes. Делается это примерно так:

GET /topic-$topicId<[0-9]+>/ controllers.AppController.topic(tid: Long)

Добавляем метод в соответствующем контроллере:

def topic(tid: Long) = Action {
  // ...
  Ok(views.html.topic(id, title, login, text, created, commentsSeq))
}

Помимо Ok также можно вернуть, например:

MovedPermanently(routes.AppController.index().toString())

Использовать созданную заранее вьюшку тоже не обязательно:

def robotsTxt() = Action {
  Ok(
    """|User-agent: *
       |Disallow: /login/
       |Host: example.org
       |Sitemap: http://example.org/sitemap.xml
       |"""
.stripMargin
  ).as("text/plain")
}

Наконец, вьюха, используемая в методе topic, должна быть определена в файле views/topics.html.scala примерно так:

@import org.joda.time.DateTime
@import commons.TextProcessor
@(topicId: Long, topicTitle: String, authorName: String,
  topicText: String, topicCreated: DateTime,
  commentsSeq: Seq[(Long, String, String, DateTime)])

@main(topicTitle) {
  <div class="img-rounded text">
    <h1>@topicTitle</h1>
    <p><span class="badge">
      @authorName @@ @topicCreated.toString("YYYY-MM-dd HH:mm:ss")
    </span></p>
    <p>@Html(TextProcessor.textToHtml(topicText))</p>
  </div>

  @for(commentInfo <- commentsSeq) {
    @* UNDER CONSTRUCTION (это - комментарий) *@
  }
}

Как видите, формат шаблонов довольно незатейлив. Заметьте, что шаблоны по сути представляют собой функции с аргументами определенных типов. В данном случае функция возвращает тип Html. Шаблоны могут вызывать друг друга. В примере выше используется шаблон main.scala.html, определенный примерно таким образом:

@(title: String)(content: Html)

<!DOCTYPE html>
<html>
  <head>
    <title>@title &#8210; Example.org</title>
    <link rel="shortcut icon" type="image/x-icon"
      href="@routes.Assets.at("images/favicon.ico")">
  </head>
  <body>
    <div class="container">
      @content
    </div>
  </body>
</html>

Файл favicon.ico, как несложно догадаться, должен лежать в public/images.

Собственно, теперь вы вполне представляете, как пишутся сайтики на Play Framework. За кадром осталась еще масса интересных вопросов, например, авторизация, хождение в базы данных, обработка форм, упаковка всего этого хозяйства в deb-пакет, и так далее. Возможно, я найду время осветить и эти вопросы. Но вообще, читайте оригинальные доки и используйте Google. Таким образом вы получите наиболее полную и актуальную информацию. Также не стесняйтесь обращаться за помощью в чатики или на StackOverflow — сообщество разработчиков вокруг Scala и Play в частности очень велико, активно и дружелюбно.

Что же касается моих впечатлений от работы с Play Framework, на момент написания этих строк я нахожу его лучшим из известных мне веб-фреймворков.

Это стабильная, обкатанная в бою технология с большим количеством людей, использующих ее. Писать сайты на Play не сложнее, чем, например, на Mojolicious. Но при этом мы также получаем строгую статическую типизацию, которая, например, не позволяет вам передать в шаблон параметры неправильного типа, или сослаться на URL, для которого нет контроллера.

Нужна поддержка со стороны IDE — нет проблем, у IntelliJ IDEA она есть. Рекомендуется купить Ultimate версию, хотя и в CI жить вполне можно. Можно вообще хоть в Vim писать, что уж тут.

Я уже не говорю о том, что Play работает на JVM, а значит (1) благодаря JIT все очень быстро превращается в машинный код, (2) проект можно запустить практически где угодно, (3) мир Java дает нам кучу библиотек практически на любой случай. О чем еще можно мечтать?

На самом деле, можно мечтать о том, чтобы Scala компилировалась чуточку быстрее. Впрочем, на нормальной машине (MacBook Pro и сравнимые железки) время компиляции получается вполне разумным.

А что вы думаете о Play Framework?

Дополнение: На случай, если у вас создалось впечатление, будто в Play Framework поддерживается только синхронная обработка запросов, отмечу, что это не так. Футуры тоже поддерживаются. Подробности можно найти в документации.

Дополнение: Знакомьтесь — ORM для Scala под названием Slick

Метки: , .


Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.