Статьи

Обновление до Play 2: Anorm и тестирование


На этот раз в прошлом году я решил, что хочу изучать Scala.
Я выбрал
Play Framework как средство обучения и добавил
CoffeeScript и
Jade . Я собрал все это, выучил кучу и
представил на Devoxx 2011 .

В январе я добавил SecureSocial, JSON Services и немного поработал над мобильным клиентом. Вскоре после этого я представил свои выводы в Jfokus . Как часть моего вышеупомянутого поста, я написал:

Прямо перед тем, как мы уехали в Jfokus, я смог заставить все работать, но не тратил столько времени, сколько хотел бы работать на мобильном клиенте. Если этот доклад будет принят для Devoxx France, я планирую потратить большую часть своего времени на улучшение мобильного клиента.

У меня были некоторые осложнения (слишком много отпуска ) с Devoxx France, и я не смог присутствовать. Чтобы восполнить это, я представил доклад на ÜberConf . Это было принято, и я начал работать над своим приложением пару недель назад. До сих пор я потратил около 8 часов на обновление до Play 2, и я надеюсь начать переписывать мобильный клиент позже на этой неделе. Я планирую использовать Cordova , jQTouch и выпустить его в App Store где-то в этом месяце.

 

Обновление до Play 2
Когда я услышал о Play 2, я подумал, что это здорово. Разработчики переписывали фреймворк, чтобы использовать Scala в своей основе, и я уже использовал Scala в своем приложении. Потом я узнал, что они собирались выбросить обратную совместимость из окна, и я застонал. «Правда? Еще один веб-фреймворк (например, Tapestry of old) завладевает пользователями и заставляет их снова учиться всему ?!» — подумал я. «Может быть, им следует называть это« Run вместо Play » , оставляя старые рамки, которые все любят, нетронутыми».

Однако, услышав об этом в Devoxx и Jfokus , я решил, что должен хотя бы попытаться мигрировать. Я скачал Play 2.0.1, создал новый проект и пошел работать.

Первое, что я узнал об обновлении с Play 1.x до Play 2.x, это то, чего нет . Это все равно, что сказать, что вы обновились с Struts 1 до Struts 2 или Tapestry 4 до Tapestry 5. Это миграция с совершенно новым проектом.

Эволюция
Я начал с того, что огляделся, чтобы узнать, документировал ли кто-нибудь подобную миграцию. Я сразу нашел два очень полезных ресурса:

Из блога Яна я научился копировать свои эволюции из проекта Play 1.x в файл conf / evolutions / default . Я изменил свой application.conf для использования PostgreSQL и написал EvolutionsTest.scala для проверки работоспособности создания таблиц.

import org.specs2.mutable._

import play.api.db.DB
import play.api.Play.current

import anorm._

import play.api.test._
import play.api.test.Helpers._

class EvolutionsTest extends Specification {

  "Evolutions" should {
    "be applied without errors" in {
      evolutionFor("default")
      running(FakeApplication()) {
        DB.withConnection {
          implicit connection =>
            SQL("select count(1) from athlete").execute()
            SQL("select count(1) from workout").execute()
            SQL("select count(1) from comment").execute()
        }
      }
      success
    }
  }
}

Затем я начал искать способ загрузки начальных данных с помощью Play 2.x. В Play 1.x вы могли использовать задание BootStrap, которое загружало бы образцы данных с YAML.

import play.jobs._
import play.Play

@OnApplicationStart
class BootStrap extends Job {

  override def doJob() {

    import models._
    import play.test._

    // Import initial data if the database is empty
    if (Athlete.count().single() == 0) {
      Yaml[List[Any]]("initial-data.yml").foreach {
        _ match {
          case a: Athlete => Athlete.create(a)
          case w: Workout => Workout.create(w)
          case c: Comment => Comment.create(c)
        }
      }
    }
  }
}

Это больше не рекомендуемая практика в Play 2. Вместо этого они рекомендуют превратить ваш YAML в код . Через 10 минут у меня был Global.scala, который загружал начальные данные.

import models._
import play.api._
import play.api.Play.current

import anorm._

object Global extends GlobalSettings {

  override def onStart(app: Application) {
    InitialData.insert()
  }
}

/**
 * Initial set of data to be loaded
 */
object InitialData {

  def date(str: String) = new java.text.SimpleDateFormat("yyyy-MM-dd").parse(str)

  def insert() {

    if (Athlete.count() == 0) {

      Seq(
        Athlete(Id(1), "[email protected]", "beer", "Matt", "Raible"),
        Athlete(Id(2), "[email protected]", "whiskey", "Trish", "McGinity")
      ).foreach(Athlete.create)

      Seq(
        Workout(Id(1), "Chainsaw Trail",
          """
            A beautiful fall ride: cool breezes, awesome views and yellow leaves.

            Would do it again in a heartbeat.
          """, 7, 90, date("2011-10-13"), 1),
        Workout(Id(2), "Monarch Lake Trail",
          "Awesome morning ride through falling yellow leaves and cool fall breezes.",
          4, 90, date("2011-10-15"), 1),
        Workout(Id(3), "Creekside to Flume to Chainsaw",
          "Awesome morning ride through falling yellow leaves and cool fall breezes.",
          12, 150, date("2011-10-16"), 2)
      ).foreach(Workout.create)

      Seq(
        Comment(1, "Jim", "Nice day for it!"),
        Comment(2, "Joe", "Love that trail."),
        Comment(2, "Jack", "Where there any kittens there?")
      ).foreach(Comment.create)
    }
  }
}

Пропавшая магия Anorm
Прежде чем начать играть в Play 2, я знал, что она потеряла часть своей магии. В конце концов, разработчики упомянули, что хотят испытать магию, и переезд в Scala позволил им сделать это. Тем не менее, я не думал, что буду скучать по Magic [T] так сильно, как я. Как и Мартин Фаулер , мне нравятся ORM, и необходимость снова использовать SQL кажется болезненной. Для Play это странное изменение, заключающееся в том, чтобы уменьшить безопасность типов на бэкэнде, но увеличить его в своих шаблонах. Ну да ладно, каждому свое. В конечном итоге я могу перейти на Squery , но я хотел провести параллельное сравнение в рамках своей миграции.

Используя вышеупомянутое руководство из сообщений в блоге Джеймса и Яна, а также Play 2.0 / Anorm Гийома , я приступил к созданию новых объектов модели. Я написал несколько SQL, набрал несколько новых искателей и перенес свои тесты из ScalaTest в новое стандартное состояние, specs2 . Переход с веб-сайта Mosh Pit на Play 2.0 для Play 2.0 очень помог в переносе тестов.

Именно тогда у меня возникли проблемы с Anorm и я выяснил, как работает синтаксический анализатор. После нескольких дней борьбы, я наконец нашел yabe-play20-scala . Поскольку я использовал учебник по yabe из Play 1.x , он был знаком и помог мне преодолеть мои проблемы. Теперь все не идеально (тренировки не упорядочены по дате публикации), но все компилируется и тесты проходят.

Для того, чтобы показать , как мало кода требуется для Anorm 1.x, проверка Workout.scala в Play 1.x против Play 2.x . Версия Play 1.x — 66 строк; Для игры 2.x требуется 193 строки. Я не знаю о вас, но я вроде как немного волшебства в моих фреймворках, чтобы уменьшить объем кода, который я должен поддерживать.

Я был приятно удивлен specs2. Прежде всего, это была простая миграция из ScalaTest. Во-вторых, FakeApplication от Play очень легко писал модульные тесты. Количество строк на моем UnitTests.scala в Play 1.x и Play 2.x практически одинаково.

Резюме
Первые несколько часов разработки с Play 2 были разочаровывающими, в основном из-за того, что я чувствовал, что мне нужно изучить все заново. Тем не менее, я был рад найти хорошие рекомендации по переходу с Play 1.x. Прошлой ночью я перенес все свои контроллеры, интегрировал Scalate и получил большую часть рендеринга моих просмотров. У меня все еще есть проблемы с
отображением ошибок валидации , но я надеюсь, что скоро это выясню. Последние 2 часа были намного веселее, и я чувствую, что мои навыки в Scala приходят вместе. Я думаю, что если бы команда разработчиков могла устранить эти первые несколько часов борьбы (и обеспечить почти мгновенную радость, как в Play 1.x), они бы действительно что-то предприняли.

Как только я выясню, как проверить и как добавить класс тела на основе URL-адреса, я напишу еще один пост об остальной части моей миграции. Совместимая с Play 2 версия
SecureSocial только что вышла этим вечером, так что я могу также интегрировать ее. В то же время я буду работать над приложением для iPhone и дописывать приложение Grails 2 для Джеймса Уорда и моего
Grails vs. Play Smackdown .