Play java or scala

Play framework + Scala — from zero to hero

В наше время набирают популярность приложения, которые выполняются в браузере, или на мобильных платформах. Надо признать, что выбор у современных программистов огромен, даже если рассматривать только лишь программирование сайтов или под телефоны. Что из этого перспективнее — тема далеко не одной статьи, поэтому не будем разводить холивар. Сегодня мы поговорим о выборе серверной технологии для своего сайта. Если вы не боитесь изучать новое — добро пожаловать под кат.

Итак. Вы собрались создать сайт. Сайт, которым будут пользоваться миллионы. У Вас есть потрясающая, оригинальная идея. Конечно же, Вы уже знакомы с веб программированием, возможно даже очень хорошо знакомы. Но Вы же знаете, что от технологий зависит успешность Вашего проекта, и Вы очень боитесь прогадать. Уже второй день вы сидите возле монитора, сравнивая технологии, читая комментарии, смотря презентации. Вы не можете ни есть, ни спать. Именно для Вас я написал эту статью. Что же я порекомендую вам? А вот что: Play framework 2.1.* + Scala.

История

Итак, давайте же посмотрим, что из себя представляет play.

Начало

Прошу простить меня пользователей windows, но так как у меня debian, я буду предоставлять решения для пользователей unix. Надеюсь, вы разберетесь.

У нас все готово, можно приступать к созданию приложения. В этом уроке мы с вами создадим простенькое todo-приложение.

$ mkdir playapps && cd playapps $ play new todolist 

Теперь пару слов о среде разработки. Вы можете пользоваться любой IDE, или любимым текстовым редактором. Я буду пользоваться intellij idea.

$ cd todolist $ play [todolist] $ run 

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

Читайте также:  Javascript установить атрибут элементу

Введение в Play и в Scala (частично)

Пора бы посмотреть, что было сгенерировано командой «play new todolist»
Если вы используете idea, то остановите сервер (Ctrl + D), и введите

Теперь, когда все нужные модули для idea созданы, мы можем открыть наш проект в IDE (этот шаг только для пользователей Intellij Idea).
Мы видим структуру проекта:

Самый важные для нас папки, это app (где лежат все компоненты MVC), и conf (с конфигом приложения и routes). Если вы уже знакомы с MVC, то можете немножко поэкспериментировать. Но для начала разберемся, почему, когда мы заходим на localhost:9000, то видим не пустую страницу.
Зайдем в conf/routes, видим там:

# Home page GET / controllers.Application.index 
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). Запись типа

означает, что функция возвращает 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")

Первая строка здесь — сигнатура функции. А далее можно писать html код, с вставками scala (которые начинаются с символа @).
Теперь вы можете снова запустить сервер в режиме разработки и немного поэксперементировать, но перед тем как продолжить отмените все изменения.

Развитие событий

А теперь мы начнем создавать todo приложение.

Шаг первый: Routes & Controller
# 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!"; 
val a = 15 // Или val a: Int = 15 val s = "Hello, Habr!" // Или val s: String = "Hello, Habr!" 

Давайте теперь попробуем запустить сервер, и посмотрим, что вышло.

Заходим на localhost:9000/tasks, и видим:

Маленькое отступление: мы не хотим, чтобы пользователи нашего приложения видели страницу, которая генерируется функцией index контроллера Action, поэтому внесем в контроллер (app/controllers/Application.scala) маленькие изменения:

Функция Redirect перенаправит нас на

GET /tasks controllers.Application.tasks 
Шаг второй: Model

Как вы уже заметили, папки controllers и views сгенерировались, а папку 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) 
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") < 

@tasks.size idea(s)

@tasks.map < task =>@if(task.ready == 0) < >else < > >
IdeaWhoWhenStatusComplete?
@task.label @task.who @task.mytime @if(task.ready==0) < unfinished >else @if(task.ready==0)< >
@form(routes.Application.deleteTask(task.id)) < >@form(routes.Application.completeTask(task.id)) < >

Add a new idea

@form(routes.Application.newTask) < @inputText(taskForm("label")) @inputText(taskForm("who")) > >

В первой строчке мы добавили в сигнатуру 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._ 
 def completeTask(id: Long) = TODO val taskForm = Form( tuple ( "label" -> nonEmptyText, "who" -> nonEmptyText ) ) 
Четвертый шаг: все вместе

Наступило время продолжить работу над главной страницей (где выводится список всех дел, а также форма для добавления дел).

    добавим импорт модели в контроллер

То, что мы передаем в views.html.index() — мы передаем в функцию, сигнатура которой находится на первой строке файла index.scala.html. Сейчас Вы можете проверить результаты нашей незаконченной работы. Откройте в браузере localhost:9000/tasks, и, если вы все делали правильно, вы увидите:

Вы можете попробовать нажать кнопку «Create», но мы еще не написали def newTask, да и базы данных для хранения информации у нас еще нет.
Для начала закончим def newTask, которая будет обрабатывать данные из формы taskForm:

    Для начала добавим пару импортов (для даты)

import java.util.Calendar import java.text.SimpleDateFormat 
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" 
# 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 в браузере.
Вы должны увидеть такую картинку:

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) > > 
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 (,,, 0)").on( 'label -> label, 'who -> who, 'mytime -> mytime ).executeUpdate() > > def delete(id: Long) < DB.withConnection < implicit c =>SQL("delete from task where 'id -> id ).executeUpdate() > > def complete(id: Long) < DB.withConnection < implicit c =>SQL("update task set ready=1 where 'id -> id ).executeUpdate() > > 

Как мы видим, написанный парсер мы используем только в функции all. Обратите внимания, что для непосредственного соединения с базой данных используется DB.withConnection, а для создания запроса мы используем функцию Anorm SQL.

Теперь, когда мы «разделались» с моделью, можно заканчивать контроллер. Нам остались две функции: delete и complete. Про удаление — без комментариев, а про complete: у каждого дела в БД есть поле «ready». Если оно 0 — то дело незаконченно. В функции complete мы будем менять его на 1. Итак, приступим:

 def deleteTask(id: Long) = Action < Task.delete(id) Redirect(routes.Application.tasks) >def completeTask(id: Long) = Action

Осталось пару штрихов. Немного доделаем фал 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=$ -DapplyEvolutions.default=true -DapplyDownEvolutions.default=true -Ddb.default.url=$ -Ddb.default.driver=org.postgresql.Driver 
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!

Источник

Оцените статью