На этот раз в прошлом году я решил, что хочу изучать 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 2.0 с Scala и Scaml, часть 1: настройка тестовой инфраструктуры, модели и персистентности с помощью Anorm от Jan Helwich
- Учебное пособие: Play Framework 2 с использованием Scala, Anorm, JSON, CoffeeScript, jQuery & Heroku, автор James Ward
Из блога Яна я научился копировать свои эволюции из проекта 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 .