Обзор фреймворка Lift на Scala

в 8:39, , рубрики: lift, scala, Веб-разработка, метки: ,

Введение

Lift считается одним из самых сложных и в тоже время мощных web-фреймворков существующих на данный момент (хоть и мало-известным), во многом потому что активно использует функциональные возможности языка Scala. Что бы его изучить нужно приложить не мало усилий. Но это того стоит, хотя бы потому что он сильно отличается от стандартных MVC фреймворков и знание альтернативных технологий расширит ваш кругозор.

В этой статье я расскажу об основных особенностях этого фреймворка.

View First

Наверное самая главная особенность — это концепт View First. Это означает что View (XML-шаблон) — точка входа в ваше приложение (в отличии от традиционных способов, где контроллер — входная точка). Scala-код не встроен во View (в Lift — невозможно писать логику на странице), но вызывается с шаблона. Благодаря концепту View First, каждый элемент (форма, поле для поиска, чат, список чего-либо и т.д.) может быть полностью изолирован от других в отдельном сниппете. Это очень сильно уменьшает связанность компонентов и позволяет легко их переиспользовать в других частях сайта.

Вот так например будет выглядеть сниппет, отвечающий за логику формы:

class Snippet = {
   def form_elements = {
    var textValue: String = ""
    var selectValue: String = ""

    def process() {
      println(textValue)
      println(selectValue)
    }
    // SHtml.onSubmit - привязывает к определнному элементу формы функцию, которая будет вызываться на сервере при сабмите формы. Функция принимает значение поля и делает с ним какую-либо логику.
    "#textField" #> SHtml.onSubmit(value => textValue = value) &
    "#selectField" #> SHtml.onSubmit(value => selectValue = value) &
    "#hidden" #> SHtml.onSubmitUnit(process) // hidden элемент нужен для вызова функции process()
  }
}

И шаблон:

<form lift="Snippet.form_elements">
     <input id="textField" type="text"/>
     <select id="selectField">
         <option>любовь</option>
         <option>деньги</option>
         <option>свобода</option>
     </select>
     <input id="hidden" type="hidden">
     <input type="submit"/>
</form>

Селекторы

Так же хочу сказать пару слов о способе «связывания» серверных переменных с View. Для этого используется механизм селекторов, который очень похож на настоящие css-селекторы. Вот пара примеров:
Нахождение элемента с id = «elemid» и установка у него атрибута «class»

def snippet = "#elemid [class]"  #> "someclass" 

с помощью метода "&" можно объединять несколько селекторов

def snippet = {
   "button [onclick]" #> Alert("some reaction") &
   "button [class]" #> "someclass"
}

Что бы в метод передать XML, который будут изменять селекторы нужно в соотстствующий тег добавить аттрибут lift (существуют и другие пути, но мне больше всех нравиться этот), в котором указывается сниппет, который будет обрабатывать этот XML.

<div lift="Snippet_class.snippet"> 
   <button> кнопка </button>
</div>

На мой взгляд это идеально-лаконичный способ связывания логики и представления. Представление не трогается вообще (только добавляется тег lift), логика в снипете читается очень легко. Дизайнеру нужно только не трогать теги «lift».

Конфигурирование

Вся конфигурация делается на Scala. Это означает что у вас нет необходимости реализовывать 40% вашего кода в XML-файлах, что бы создать гибкую конфигурацию. Так же это означает что компилятор вам будет подсказывать если вы где-то указали невалидный параметр в конфигурации. Это очень удобно когда у проекта большая и длинная конфигурация.

Безопасность

Лифт гарантирует защиту от многих распространенных уязвимостей по версии OWASP. Самое забавное что вам не нужно ничего для этого делать, достаточно просто писать код на Lift. Вот что говорит Расмус Лердорф (человек, который отвечает за безопасность в Yahoo) о FourSquare (проект, который полностью написан на Lift):

«Four stars to @foursquare — 1st site in a while I have taken a good look at that didn't have a single security issue (that I could find)»

twitter.com/rasmus/status/5929904263.

Это означает что разработчику не нужно заботиться о безопасности и он может полностью сконцентрироваться на бизнес-логике.

Поддержка Ajax

Вот как можно реализовать обычную ajax-кнопку:

class Snippet {
   def button = { 
      def process() =  {
         println("сохраняем что нибудь в базу на сервере");
         Alert("алерт клиенту, что то успешно сохранено в базу") 
      }

      "button [onclick]" #> SHtml.ajaxInvoke(process)
   }
}

И xml-шаблон:

<button lift="Snippet.button"> Сохранить что-либо в базу </button>

При нажатии на кнопку произойдет ajax-вызов на сервер, на сервере вызовется функция process и вернется какая-либо javaScript-реакция. Минимум кода, максимум логики. Этот принцип прослеживается во всем фреймворке.

Поддержка Comet

Lift имеет очень хорошую поддержку Comet, это практически является гордостью фреймворка. Вот как будет выглядеть простой пример часов, которые обновляются с сервера раз в 10 секунд:

object Tick

class Clock extends CometActor {
  Schedule.schedule(this, Tick, 10 seconds)

  def render = "#time *" #> now.toString

  override def lowPriority = {
    case Tick => 
      val js = SetHtml("time", Text(now.toString))
      partialUpdate(js) // пушим js-код в браузер клиенту
      Schedule.schedule(this, Tick, 10 seconds) // шедулим событие
  }
}

Шаблон

<div lift="comet?type=Clock">
   <span id="time"></span>
</div>

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

<div lift="LazyLoad">
   <div lift="Snippet.lazy_content"> 
      какой-либо тяжелый контент, который будет подгружаться лениво 
   </div>
</div>

Сниппет:

def lazy_content = {
   Thread.sleep(3000) // задержка для наглядности
   "div *" #> "я загрузилось на 3 секунды позже всей страницы"
}

Компактность

Когда вы пишите на Lift вы пишите раза в 2-3 меньше кода, чем если бы писали на другом фреймворке (например один прогер переписал свой проект с Play на Lift, на Play у него реализация заняла 24k строк, а на Lift 6k ссылка ). А если сравнивать с фреймворками на java типа Spring MVC / Struts то разница будет еще больше. Причем весь код является статически типизированным.

Заключение

Я описал только небольшую часть функциональности. В одной статье невозможно раскрыть все аспекты такого большого фреймворка. Для тех кто хочет копать глубже вот ссылки:
liftweb.net/ — основной сайт
www.assembla.com/wiki/show/liftweb — wiki
exploring.liftweb.net/onepage/ — книга, написанная автором фреймворка

Автор: mnemosha

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js