В наше время набирают популярность приложения, которые выполняются в браузере, или на мобильных платформах. Надо признать, что выбор у современных программистов огромен, даже если рассматривать только лишь программирование сайтов или под телефоны. Что из этого перспективнее — тема далеко не одной статьи, поэтому не будем разводить холивар. Сегодня мы поговорим о выборе серверной технологии для своего сайта. Если вы не боитесь изучать новое — добро пожаловать под кат.
Итак. Вы собрались создать сайт. Сайт, которым будут пользоваться миллионы. У Вас есть потрясающая, оригинальная идея. Конечно же, Вы уже знакомы с веб программированием, возможно даже очень хорошо знакомы. Но Вы же знаете, что от технологий зависит успешность Вашего проекта, и Вы очень боитесь прогадать. Уже второй день вы сидите возле монитора, сравнивая технологии, читая комментарии, смотря презентации. Вы не можете ни есть, ни спать. Именно для Вас я написал эту статью. Что же я порекомендую вам? А вот что: Play framework 2.1.* + Scala.
Лично мой путь, как backend разработчика был таков: php(Yii) -> RoR -> Play. Я думаю, не надо объяснять первую стрелочку, а про вторую я расскажу. До разработки на php я разрабатывал на java. Правда не EE, я разрабатывал gui на swing. Когда пришлось переквалифицироваться в backend разработчика, первая мысль была: java. Возможно, если бы я увидел Play еще тогда, то не было бы цепочки php -> RoR -> Play. Но для того времени play был еще сыроват, да и сейчас я не использую его с java. Но беглый взгляд на ЕЕ, и она была отброшена (видимо, не для меня это — enterprise). Ну а по поводу второй стрелочки. Почему же я ушел с RoR, с рельсов, которые все так хвалят? Ответ:
Несмотря на то, что у меня есть несколько проектов на RoR, самим языком я владею недостаточно хорошо. И когда появлялись проблемы, решение которых уходило дальше работы с фреймворком (математические вычисления и т.д.), то мне надо было все дальше погружаться в язык. А я этого не хотел. И вот, в какой-то момент, бороздя просторы интернета, я нашел чудо, под названием Scala.
Я не хочу, чтобы статья превратилась в холивар между императивным и функциональным программированием (да и вообще не в какой холивар). Даже если вас не устраивает Scala — вполне можно использовать play с java, хотя я бы порекомендовал каждому джависту (да и не только джавистом) познакомиться с этим языком. Почему я выбрал его, а не руби? Во-первых, он выполняется JVM, что дает доступ из него ко всему, что доступно из java. Во-вторых, он выполняется быстрее чем руби (благодаря JVM). Так зачем же, скажете Вы, тебе скала, просто используй java. Дело в том, что почти во всех случаях, и в самом фреймворке play, и не только код на скале будет занимать меньше места, чем код javа. И при этом, заметьте, производительность не хуже, возможно даже скала немного шустрее. Собственно, поэтому я и выбрал скалу. Даже если вы никогда не видели код на scale, с этим туториалом у вас не должно возникнуть осложнений.
Итак, давайте же посмотрим, что из себя представляет play.
Прошу простить меня пользователей windows, но так как у меня debian, я буду предоставлять решения для пользователей unix. Надеюсь, вы разберетесь.
Подготовка:
У нас все готово, можно приступать к созданию приложения. В этом уроке мы с вами создадим простенькое todo-приложение.

Теперь пару слов о среде разработки. Вы можете пользоваться любой IDE, или любимым текстовым редактором. Я буду пользоваться intellij idea.
Мы запустили наше приложение в режиме разработки (development mode). Перейдя по адресу localhost:9000. Если вы все правильно сделали, то должны увидеть вот это:

Пора бы посмотреть, что было сгенерировано командой «play new todolist»
Если вы используете idea, то остановите сервер (Ctrl + D), и введите
Теперь, когда все нужные модули для idea созданы, мы можем открыть наш проект в IDE (этот шаг только для пользователей Intellij Idea).
Мы видим структуру проекта:

Самый важные для нас папки, это app (где лежат все компоненты MVC), и conf (с конфигом приложения и routes). Если вы уже знакомы с MVC, то можете немножко поэкспериментировать. Но для начала разберемся, почему, когда мы заходим на localhost:9000, то видим не пустую страницу.
Зайдем в conf/routes, видим там:
Исходя из этого, идем в app/controllers/Application.scala:
Что же это за магическая строчка в def index, которая позволяет нам увидеть страницу, полную информации. Давайте розбираться. Но для начала хочу объяснить (для тех, кто не знаком с Scala). Запись типа
означает, что функция возвращает Action (это часть фреймворка, не будем углубляться; просто скажу, что Action обрабатывает запрос, и отправляет ответ в браузер). Да, и еще. Как вы уже успели заметить, в Scala точки с запятой необязательны (только если вы записываете несколько выражений в одну строку, например:
).
Теперь разберемся с строкой
Первое: каждое выражение в скале имеет свое значение. Так функция index возвращает значение этой единственной строки.
Второе: разберем саму строку. Функция «Ok» создает «200 OK» ответ, заполненный html контентом, из view`а под названием index.scala.html (дефолтные views в play на Scala template language; если вы хотите — можете использовать haml, или еще что-то). Давайте откроем app/views/index.scala.html:
Первая строка здесь — сигнатура функции. А далее можно писать html код, с вставками scala (которые начинаются с символа @).
Теперь вы можете снова запустить сервер в режиме разработки и немного поэксперементировать, но перед тем как продолжить отмените все изменения.
А теперь мы начнем создавать todo приложение.
Для начала настроим файл conf/routes:
Кроме стандартных, мы добавили еще 4 rout'a. Они позволят нам просматривать задания (первый), создавать задания (второй), удалять задания (третий), и делать задание завершенным (четвертый).
Теперь для каждого пути надо создать действия в app/controllers/Application.scala:
Мы пока еще не знаем, что будут делать эти функции, поэтому мы используем встроенную функцию TODO, которая вернет HTTP ответ «501 Not Implemented». Да, и еще: Scala — строго типизированный язык, но объявление метода переменных может быть непривычно для Java/C++ программистов.
Java код:
Scala код:
Давайте теперь попробуем запустить сервер, и посмотрим, что вышло.
Заходим на localhost:9000/tasks, и видим:

Маленькое отступление: мы не хотим, чтобы пользователи нашего приложения видели страницу, которая генерируется функцией index контроллера Action, поэтому внесем в контроллер (app/controllers/Application.scala) маленькие изменения:
Функция Redirect перенаправит нас на
Как вы уже заметили, папки controllers и views сгенерировались, а папку models нам придется создать вручную. В корне приложения из консоли:
Теперь создадим в ней файл Task.scala. Он и станет нашей моделью. Итак, app/models/Task.scala:
Обратите внимание, запись
равноценна такой записи на java:
А теперь давайте немного отвлечемся к модели, и перейдем к
Изменим содержимое файла app/views/index.scala.html на:
Обратите внимание на:
В первой строчке мы добавили в сигнатуру 2 параметра: список заданий, и «какой-то странный» taskForm. О нем мы поговорим чуть позже.
Далее идет импорт. Надо отметить, что запись в scala «import helper._» аналогична записи на java «import helper.*». Этот импорт предоставляет нам функцию form, которая создает тег с атрибутами action и method, а также функцию inputText, который создает несколько полей: input для ввода, label объясняет, что вводить, и еще label`ы, которые сообщают об ошибках.
Все остальные записи после @, на мой взгляд, интуитивно понятны.
Перейдем к «неведомой» taskForm. Внесем некоторые модификации в файл app/controllers/Application.scala:
Наступило время продолжить работу над главной страницей (где выводится список всех дел, а также форма для добавления дел).
Шаблон для нее у нас уже изготовлен (app/views/index.scala.html). Теперь надо доработать контроллер и модель. Начнем мы с контроллера:
То, что мы передаем в views.html.index() — мы передаем в функцию, сигнатура которой находится на первой строке файла index.scala.html. Сейчас Вы можете проверить результаты нашей незаконченной работы. Откройте в браузере localhost:9000/tasks, и, если вы все делали правильно, вы увидите:

Вы можете попробовать нажать кнопку «Create», но мы еще не написали def newTask, да и базы данных для хранения информации у нас еще нет.
Для начала закончим def newTask, которая будет обрабатывать данные из формы taskForm:
Для человека, не знакомого с Scala, синтаксис данного кода будет понятно трудно, поэтому я просто объясню, что он делает. Из запроса мы получаем данные формы, которые мы обрабатываем (функцией bindFromRequest.fold()). Если есть ошибки в запросе, то выполняется функция BadRequest, которая останавливает процесс, возвращает нас на главную страницу и выводит все ошибки. Далее нам надо обработать еще и само задание (label), и того, кто создал идею (who). А также мы находим время создания (заметьте, оно не отправляется с формой, просто записывается время создания задания), и вызываем функцию Task.create(). Как и функцию Task.all, мы скоро допишем в файле app/models/Task.scala. Ну и теперь, когда все создано, мы переправляем пользователя на главную страницу.
Наступило время обратиться к базе данных.
Что нам осталось сделать? Подключиться-таки к базе данных, дописать методы модели и контроллера. Займемся базой данных.
Теперь, когда мы «разделались» с моделью, можно заканчивать контроллер. Нам остались две функции: delete и complete. Про удаление — без комментариев, а про complete: у каждого дела в БД есть поле «ready». Если оно 0 — то дело незаконченно. В функции complete мы будем менять его на 1. Итак, приступим:
Осталось пару штрихов. Немного доделаем фал app/views/index.scala.html.
Вот этот код:
заменим на:
Поздравляю! Если вы честно следовали по статье, то сейчас у вас есть полностью работающее приложение. Вместе с делами оно может выглядеть вот так:

Теперь перейдем к развертыванию приложения. Я буду использовать git и heroku. Но для начала надо создать Procfile в корне приложения (в папке todolist):
Так же измените project/Build.scala:
Это надо сделать потому, что мы использовали базу данных H2, а на heroku база данных — PostgreSQL. Наконец-то мы можем перейти к непосредственному развертыванию (у вас должно быть установлено heroku, и вы должны быть зарегестрированы на сайте heroku.com), если нет:
):
Подведем итоги. Сегодня мы познакомились с языком программирования Scala (не все, естественно), с фреймворком Play. Пользоваться им или нет — решать вам. Я на простом примере показал всю простоту их использования. Мне бы хотелось, чтобы каждый кто прочел эту статью начал пользоваться приобретенными знаниями и развивать их. Пишите на scala с play!
Play
Scala
Уроки по ФП на скале на coursera
Scala + Intellij Idea
Это приложение на github
Итак. Вы собрались создать сайт. Сайт, которым будут пользоваться миллионы. У Вас есть потрясающая, оригинальная идея. Конечно же, Вы уже знакомы с веб программированием, возможно даже очень хорошо знакомы. Но Вы же знаете, что от технологий зависит успешность Вашего проекта, и Вы очень боитесь прогадать. Уже второй день вы сидите возле монитора, сравнивая технологии, читая комментарии, смотря презентации. Вы не можете ни есть, ни спать. Именно для Вас я написал эту статью. Что же я порекомендую вам? А вот что: Play framework 2.1.* + Scala.
История
Лично мой путь, как backend разработчика был таков: php(Yii) -> RoR -> Play. Я думаю, не надо объяснять первую стрелочку, а про вторую я расскажу. До разработки на php я разрабатывал на java. Правда не EE, я разрабатывал gui на swing. Когда пришлось переквалифицироваться в backend разработчика, первая мысль была: java. Возможно, если бы я увидел Play еще тогда, то не было бы цепочки php -> RoR -> Play. Но для того времени play был еще сыроват, да и сейчас я не использую его с java. Но беглый взгляд на ЕЕ, и она была отброшена (видимо, не для меня это — enterprise). Ну а по поводу второй стрелочки. Почему же я ушел с RoR, с рельсов, которые все так хвалят? Ответ:
- из-за производительности
- из-за языка
Несмотря на то, что у меня есть несколько проектов на RoR, самим языком я владею недостаточно хорошо. И когда появлялись проблемы, решение которых уходило дальше работы с фреймворком (математические вычисления и т.д.), то мне надо было все дальше погружаться в язык. А я этого не хотел. И вот, в какой-то момент, бороздя просторы интернета, я нашел чудо, под названием Scala.
Я не хочу, чтобы статья превратилась в холивар между императивным и функциональным программированием (да и вообще не в какой холивар). Даже если вас не устраивает Scala — вполне можно использовать play с java, хотя я бы порекомендовал каждому джависту (да и не только джавистом) познакомиться с этим языком. Почему я выбрал его, а не руби? Во-первых, он выполняется JVM, что дает доступ из него ко всему, что доступно из java. Во-вторых, он выполняется быстрее чем руби (благодаря JVM). Так зачем же, скажете Вы, тебе скала, просто используй java. Дело в том, что почти во всех случаях, и в самом фреймворке play, и не только код на скале будет занимать меньше места, чем код javа. И при этом, заметьте, производительность не хуже, возможно даже скала немного шустрее. Собственно, поэтому я и выбрал скалу. Даже если вы никогда не видели код на scale, с этим туториалом у вас не должно возникнуть осложнений.
Итак, давайте же посмотрим, что из себя представляет play.
Начало
Прошу простить меня пользователей windows, но так как у меня debian, я буду предоставлять решения для пользователей unix. Надеюсь, вы разберетесь.
Подготовка:
- Вам потребуется JDK 6 или выше.
- Play binary package
У нас все готово, можно приступать к созданию приложения. В этом уроке мы с вами создадим простенькое todo-приложение.
$ mkdir playapps && cd playapps
$ play new todolist

Теперь пару слов о среде разработки. Вы можете пользоваться любой IDE, или любимым текстовым редактором. Я буду пользоваться intellij idea.
$ cd todolist
$ play
[todolist] $ run
Мы запустили наше приложение в режиме разработки (development mode). Перейдя по адресу localhost:9000. Если вы все правильно сделали, то должны увидеть вот это:

Введение в Play и в Scala (частично)
Пора бы посмотреть, что было сгенерировано командой «play new todolist»
Если вы используете idea, то остановите сервер (Ctrl + D), и введите
[todolist] $ idea
Теперь, когда все нужные модули для idea созданы, мы можем открыть наш проект в IDE (этот шаг только для пользователей Intellij Idea).
Мы видим структуру проекта:

Самый важные для нас папки, это app (где лежат все компоненты MVC), и conf (с конфигом приложения и routes). Если вы уже знакомы с MVC, то можете немножко поэкспериментировать. Но для начала разберемся, почему, когда мы заходим на localhost:9000, то видим не пустую страницу.
Зайдем в conf/routes, видим там:
# Home page
GET / controllers.Application.index
Исходя из этого, идем в app/controllers/Application.scala:
package controllers
import play.api._
import play.api.mvc._
object Application extends Controller {
def index = Action {
Ok(views.html.index("Your new application is ready."))
}
}
Что же это за магическая строчка в def index, которая позволяет нам увидеть страницу, полную информации. Давайте розбираться. Но для начала хочу объяснить (для тех, кто не знаком с Scala). Запись типа
def index = Action {}
означает, что функция возвращает Action (это часть фреймворка, не будем углубляться; просто скажу, что Action обрабатывает запрос, и отправляет ответ в браузер). Да, и еще. Как вы уже успели заметить, в Scala точки с запятой необязательны (только если вы записываете несколько выражений в одну строку, например:
if(a>b) var c =5; var d=15 else var f = 10
).
Теперь разберемся с строкой
Ok(views.html.index("Your new application is ready."))
Первое: каждое выражение в скале имеет свое значение. Так функция index возвращает значение этой единственной строки.
Второе: разберем саму строку. Функция «Ok» создает «200 OK» ответ, заполненный html контентом, из view`а под названием index.scala.html (дефолтные views в play на Scala template language; если вы хотите — можете использовать haml, или еще что-то). Давайте откроем app/views/index.scala.html:
@(message: String)
@main("Welcome to Play 2.1") {
@play20.welcome(message)
}
Первая строка здесь — сигнатура функции. А далее можно писать html код, с вставками scala (которые начинаются с символа @).
Теперь вы можете снова запустить сервер в режиме разработки и немного поэксперементировать, но перед тем как продолжить отмените все изменения.
Развитие событий
А теперь мы начнем создавать todo приложение.
Шаг первый: Routes & Controller
Для начала настроим файл conf/routes:
# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~
# Home page
GET / controllers.Application.index
# Map static resources from the /public folder to the /assets URL path
GET /assets/*file controllers.Assets.at(path="/public", file)
# Tasks
GET /tasks controllers.Application.tasks
POST /tasks controllers.Application.newTask
POST /tasks/:id/delete controllers.Application.deleteTask(id: Long)
POST /tasks/:id/complete controllers.Application.completeTask(id: Long)
Кроме стандартных, мы добавили еще 4 rout'a. Они позволят нам просматривать задания (первый), создавать задания (второй), удалять задания (третий), и делать задание завершенным (четвертый).
Теперь для каждого пути надо создать действия в app/controllers/Application.scala:
def tasks = TODO
def newTask = TODO
def deleteTask(id: Long) = TODO
def completeTask(id: Long) = TODO
Мы пока еще не знаем, что будут делать эти функции, поэтому мы используем встроенную функцию TODO, которая вернет HTTP ответ «501 Not Implemented». Да, и еще: Scala — строго типизированный язык, но объявление метода переменных может быть непривычно для Java/C++ программистов.
Java код:
int a = 15;
String s = "Hello, Habr!";
Scala код:
val a = 15 // Или val a: Int = 15
val s = "Hello, Habr!" // Или val s: String = "Hello, Habr!"
Давайте теперь попробуем запустить сервер, и посмотрим, что вышло.
[todolist] $ run
Заходим на localhost:9000/tasks, и видим:

Маленькое отступление: мы не хотим, чтобы пользователи нашего приложения видели страницу, которая генерируется функцией index контроллера Action, поэтому внесем в контроллер (app/controllers/Application.scala) маленькие изменения:
def index = Action {
Redirect(routes.Application.tasks)
}
Функция Redirect перенаправит нас на
GET /tasks controllers.Application.tasks
Шаг второй: Model
Как вы уже заметили, папки controllers и views сгенерировались, а папку models нам придется создать вручную. В корне приложения из консоли:
$ mkdir app/models
Теперь создадим в ней файл Task.scala. Он и станет нашей моделью. Итак, app/models/Task.scala:
package models
case class Task(id: Long, label: String, who: String, mytime: String, ready: Short)
object Task {
def all(): List[Task] = Nil
def create(label: String, who: String, time: String) {}
def delete(id: Long) {}
def complete(id: Long) {}
}
Обратите внимание, запись
case class Task(id: Long, label: String, who: String, mytime: String, ready: Int)
равноценна такой записи на java:
public class Task{
private long id;
private String label;
private String who;
private Int ready;
public Task(long id, String label, String who, Int ready){
this.id = id;
this.label = label;
this.who = who;
this.ready = ready;
}
public long getId(){
return id;
}
public void setId(long id){
this.id = id;
}
//и еще 3 геттера и сеттера
А теперь давайте немного отвлечемся к модели, и перейдем к
Третий шаг: Views
Изменим содержимое файла app/views/index.scala.html на:
@(tasks: List[Task], taskForm: Form[(String, String)])
@import helper._
@main("Todo list") {
<h1>@tasks.size idea(s)</h1>
<table style="text-align: center; border: 1px double black; width: 100%;">
<tr><th>Idea</th><th>Who</th><th>When</th><th>Status</th><th>Complete?</th></tr>
@tasks.map { task =>
@if(task.ready == 0) {
<tr>
}else{
<tr style="color: red; font-weight: bold; font-size: 16px;">
}
<td>@task.label</td>
<td>@task.who</td>
<td>@task.mytime</td>
<td>@if(task.ready==0) {
unfinished
} else {
finished
}</td>
<td>
<table align=center>
<td>@form(routes.Application.deleteTask(task.id)) {
<input type="submit" value="Delete" onclick="return confirm('Are you sure?');">
}</td>
@if(task.ready==0){
<td>@form(routes.Application.completeTask(task.id)){
<input type="submit" value="done?" onclick="return confirm('Are you sure?');">
}</td>
}
</table>
</td>
</tr>
}
</table>
<h2>Add a new idea</h2>
@form(routes.Application.newTask) {
@inputText(taskForm("label"))
@inputText(taskForm("who"))
<input type="submit" value="Create">
}
}
Обратите внимание на:
В первой строчке мы добавили в сигнатуру 2 параметра: список заданий, и «какой-то странный» taskForm. О нем мы поговорим чуть позже.
Далее идет импорт. Надо отметить, что запись в scala «import helper._» аналогична записи на java «import helper.*». Этот импорт предоставляет нам функцию form, которая создает тег с атрибутами action и method, а также функцию inputText, который создает несколько полей: input для ввода, label объясняет, что вводить, и еще label`ы, которые сообщают об ошибках.
Все остальные записи после @, на мой взгляд, интуитивно понятны.
Перейдем к «неведомой» taskForm. Внесем некоторые модификации в файл app/controllers/Application.scala:
- Добавим пару импортов для роботы с формами:
import play.api.data._ import play.api.data.Forms._
- Изготовим саму taskForm:
def completeTask(id: Long) = TODO val taskForm = Form( tuple ( "label" -> nonEmptyText, "who" -> nonEmptyText ) )
Четвертый шаг: все вместе
Наступило время продолжить работу над главной страницей (где выводится список всех дел, а также форма для добавления дел).
Шаблон для нее у нас уже изготовлен (app/views/index.scala.html). Теперь надо доработать контроллер и модель. Начнем мы с контроллера:
- добавим импорт модели в контроллер
import models.Task
- закончим работу с def tasks
def tasks = Action { Ok(views.html.index(Task.all(), taskForm)) }
То, что мы передаем в views.html.index() — мы передаем в функцию, сигнатура которой находится на первой строке файла index.scala.html. Сейчас Вы можете проверить результаты нашей незаконченной работы. Откройте в браузере localhost:9000/tasks, и, если вы все делали правильно, вы увидите:

Вы можете попробовать нажать кнопку «Create», но мы еще не написали def newTask, да и базы данных для хранения информации у нас еще нет.
Для начала закончим def newTask, которая будет обрабатывать данные из формы taskForm:
- Для начала добавим пару импортов (для даты)
import java.util.Calendar import java.text.SimpleDateFormat
- а теперь напишем саму функцию newTaks
def newTask = Action { implicit request => taskForm.bindFromRequest.fold( errors => BadRequest(views.html.index(Task.all(), errors)), x=>x match { case(label,who) => { // Получаем текущее время val today = Calendar.getInstance().getTime() val timeFormat = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss") val time = timeFormat.format(today) //---------------------------- Task.create(label, who, time) Redirect(routes.Application.tasks) } } ) }
Для человека, не знакомого с Scala, синтаксис данного кода будет понятно трудно, поэтому я просто объясню, что он делает. Из запроса мы получаем данные формы, которые мы обрабатываем (функцией bindFromRequest.fold()). Если есть ошибки в запросе, то выполняется функция BadRequest, которая останавливает процесс, возвращает нас на главную страницу и выводит все ошибки. Далее нам надо обработать еще и само задание (label), и того, кто создал идею (who). А также мы находим время создания (заметьте, оно не отправляется с формой, просто записывается время создания задания), и вызываем функцию Task.create(). Как и функцию Task.all, мы скоро допишем в файле app/models/Task.scala. Ну и теперь, когда все создано, мы переправляем пользователя на главную страницу.
Наступило время обратиться к базе данных.
Что нам осталось сделать? Подключиться-таки к базе данных, дописать методы модели и контроллера. Займемся базой данных.
- В файле conf/application.conf раскомментируйте или добавьте строки
db.default.driver=org.h2.Driver db.default.url="jdbc:h2:mem:play"
Как вы уже поняли, мы будем использовать базу данных H2.
- В фреймворке play существует система еволюций. Они используются для создания структуры базы данных. Создайте файл conf/evolutions/default/1.sql с таким содержанием:
# Tasks schema # --- !Ups CREATE SEQUENCE task_id_seq; CREATE TABLE task ( id integer NOT NULL DEFAULT nextval('task_id_seq'), label varchar(2000), who varchar(40), mytime varchar(100), ready integer ); # --- !Downs DROP TABLE task; DROP SEQUENCE task_id_seq;
Сейчас настало самое время открыть страницу localhost:9000/tasks в браузере.
Вы должны увидеть такую картинку:
Play нашел нашу еволюцию, и хочет ее выполнить. Нажмите «Apply this script now».
- Для «связи» с базой данных мы будем использовать anorm для того, чтобы преобразовать JDBC сырой ResultSet в значение Task. Для начала добавим импорты:
import anorm._ import anorm.SqlParser._
А теперь добавим основной код:
val task = { get[Long]("id") ~ get[String]("label") ~ get[String]("who") ~ get[String]("mytime") ~ get[Int]("ready") map { case id~label~who~mytime~ready => Task(id, label, who, mytime, ready) } }
Тут task выступает парсером значений, которые мы будем получать от JDBC ResultSet
- Теперь, когда парсер написан, мы можем смело приступать к подсоединению к базе данных. Добавим импорты:
import play.api.db._ import play.api.Play.current
А теперь заканчиваем функции all, create, delete и complete:
def all(): List[Task] = DB.withConnection { implicit c => SQL("select * from task").as(task *) } def create(label: String, who: String, mytime: String) { DB.withConnection { implicit c => SQL("insert into task (label,who,mytime,ready) values ({label},{who},{mytime}, 0)").on( 'label -> label, 'who -> who, 'mytime -> mytime ).executeUpdate() } } def delete(id: Long) { DB.withConnection { implicit c => SQL("delete from task where id = {id}").on( 'id -> id ).executeUpdate() } } def complete(id: Long) { DB.withConnection { implicit c => SQL("update task set ready=1 where id = {id}").on( 'id -> id ).executeUpdate() } }
Как мы видим, написанный парсер мы используем только в функции all. Обратите внимания, что для непосредственного соединения с базой данных используется DB.withConnection, а для создания запроса мы используем функцию Anorm SQL.
Сейчас уже вы можете создавать дела. Попробуйте перейти на localhost.9000/tasks и создать несколько дел. Но ни удалять, ни делать завершенными вы дела еще не можете.
Теперь, когда мы «разделались» с моделью, можно заканчивать контроллер. Нам остались две функции: delete и complete. Про удаление — без комментариев, а про complete: у каждого дела в БД есть поле «ready». Если оно 0 — то дело незаконченно. В функции complete мы будем менять его на 1. Итак, приступим:
def deleteTask(id: Long) = Action {
Task.delete(id)
Redirect(routes.Application.tasks)
}
def completeTask(id: Long) = Action {
Task.complete(id)
Redirect(routes.Application.tasks)
}
Осталось пару штрихов. Немного доделаем фал app/views/index.scala.html.
Вот этот код:
@inputText(taskForm("label"))
@inputText(taskForm("who"))
заменим на:
@inputText(taskForm("label"), args = 'size -> 55, 'placeholder -> "Idea")
@inputText(taskForm("who"), args = 'placeholder -> "Your name")
Поздравляю! Если вы честно следовали по статье, то сейчас у вас есть полностью работающее приложение. Вместе с делами оно может выглядеть вот так:

Теперь перейдем к развертыванию приложения. Я буду использовать git и heroku. Но для начала надо создать Procfile в корне приложения (в папке todolist):
web: target/start -Dhttp.port=${PORT} -DapplyEvolutions.default=true -DapplyDownEvolutions.default=true -Ddb.default.url=${DATABASE_URL} -Ddb.default.driver=org.postgresql.Driver
Так же измените project/Build.scala:
import sbt._
import Keys._
import play.Project._
object ApplicationBuild extends Build {
val appName = "todolist"
val appVersion = "1.0-SNAPSHOT"
val appDependencies = Seq(
// Add your project dependencies here,
jdbc,
anorm,
"postgresql" % "postgresql" % "8.4-702.jdbc4"
)
val main = play.Project(appName, appVersion, appDependencies).settings(
// Add your own project settings here
)
}
Это надо сделать потому, что мы использовали базу данных H2, а на heroku база данных — PostgreSQL. Наконец-то мы можем перейти к непосредственному развертыванию (у вас должно быть установлено heroku, и вы должны быть зарегестрированы на сайте heroku.com), если нет:
$ wget -qO- https://toolbelt.heroku.com/install-ubuntu.sh | sh
$ heroku login
):
$ git init
$ git add .
$ git commit -m "init"
$ heroku create --stack cedar
$ git push heroku master
$ heroku open
Итоги
Подведем итоги. Сегодня мы познакомились с языком программирования Scala (не все, естественно), с фреймворком Play. Пользоваться им или нет — решать вам. Я на простом примере показал всю простоту их использования. Мне бы хотелось, чтобы каждый кто прочел эту статью начал пользоваться приобретенными знаниями и развивать их. Пишите на scala с play!
Полезные ссылки:
Play
Scala
Уроки по ФП на скале на coursera
Scala + Intellij Idea
Это приложение на github