Как стать автором
Обновить

Простейшее web-приложение на LiftWeb

Время на прочтение 4 мин
Количество просмотров 3.4K
Прочитал недавно один топик, немного поискал в интернете утилиту, которая бы выводила расшифровку флагов процессора, не нашел, а потому взял и написал свою. Используя LiftWeb.

Процессом написания я и хочу поделиться. Сразу предупреждаю, внутри много текста, но можно перейти непосредственно к практике, пропустив теоретическую часть.

Итак, нам понадобятся:
  • maven (я использовал версию 3)
  • подключение к Интернет (мавен будет много чего качать)
  • IDE либо текстовый редактор

Для создания скелета приложения используем maven:
mvn archetype:generate -DarchetypeGroupId=net.liftweb -DarchetypeArtifactId=lift-archetype-blank_2.8.1 -DarchetypeVersion=2.2-RC6 -DarchetypeRepository=http://scala-tools.org/repo-snapshots -DremoteRepositories=http://scala-tools.org/repo-snapshots -DgroupId=app.example -DartifactId=cpuflags -Dpackage=app.example.cpuflags

И проект можно уже запустить! mvn jetty:run и заходим на localhost:8080


Да, пока маловато функционала, и мы это исправим.

Но в начале немного теории

Проект представляет собой обычное J2EE web-приложение. В наличии имеется web.xml с единственным назначением — загружать LiftFilter, который и будет заниматься всей магией.

При инициализации фильтр вызывает метод bootstrap.liftweb.Boot.boot, который отвечает за настройку фреймворка. В частности, описывает, в каких пакетах искать снипеты (о них — дальше). Также в этом только что сгенеренном методе создается меню, но оно нам не нужно и его можно удалить.

Теперь о жизненном цикле запроса. Когда происходит обращение к какому-то URL, фреймворк находит соответствующий темплейт, рендерит его и отдает результат пользователю. Правила «нахождения» темплейта могут быть изменены в boot-методе, но это тема для отдельной статьи. Для начала нам хватит знания, что темплейты расположены в каталоге webapp и мапятся один к одному на URL запроса, то есть при запросе localhost:8080/index.html будет отрендерен темплейт index.html и так далее.

Каждый темплейт представляет собой xml-файл. Вот сгенеренный index.html для нашего проекта:
<lift:surround with="default" at="content">
    <h2>Welcome to your project!</h2>
    <p>
        <lift:helloWorld.howdy>
            <span>Welcome to cpuflags at <b:time/></span>
        </lift:helloWorld.howdy>
    </p>
</lift:surround>

Тут используется важнейшая часть LiftWeb — сниппеты. Сниппет — это scala-метод, который принимает на вход html, каким-то образом его обрабатывает и выдает на выходе.

Например, сниппет lift:surround выполняет важнейшую функцию: маппинг темплейта на html. В данном примере происходит следующее:
  1. в каталоге templates-hidden ищется файл с именем default.html (имя берется из атрибута with, плюс расширение html)
    Вот этот файл:
    <html xmlns="http://www.w3.org/1999/xhtml" xmlns:lift="http://liftweb.net/">
      <head>
        <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
        <meta name="description" content="" />
        <meta name="keywords" content="" />
        <title>app.example:cpuflags:1.0-SNAPSHOT</title>
        <script id="jquery" src="/classpath/jquery.js" type="text/javascript"></script>
      </head>
      <body>
        <lift:bind name="content" />
        <lift:Menu.builder />
        <lift:msgs/>
      </body>
    </html>
    
  2. внутри этого файла ищется нода lift:bind с именем content (имя берется из атрибута at)
  3. содержимое lift:bind заменяется на содержимое lift:surround из темплейта

Сниппет lift:helloWorld.howdy — это уже не стандартный сниппет, а сгенеренный. Когда LiftWeb встречает ссылку на сниппет, и если это не стандартный сниппет, то происходит поиск среди нашего scala-кода. Для этого в boot-методе и указывался пакет app.example.cpuflags. В данном случае создается новый экземпляр класса app.example.cpuflags.HelloWorld и у него вызывается метод howdy.

Вот этот метод:
  def howdy(in: NodeSeq): NodeSeq = 
    Helpers.bind("b", in, "time" -> (new _root_.java.util.Date).toString)

Тут все просто: внутри in (который и есть поступающий на вход html) ищется нода b:time (которая есть в темплейте) и заменяется на текущую дату. Результат возвращается методом.

Хватит теории, пора приступать к практике

Меняем темплейт:
<lift:surround with="default" at="content">
    <h2>Enter your CPU flags</h2>
    <form method="get">
        <input type="text" name="flags"/>
        <input type="submit"/>
    </form>
    <table cellpadding="10">
        <lift:CpuInfo.flags>
            <tr>
                <td><flag:name/></td> <td><flag:description/></td>
            </tr>
        </lift:CpuInfo.flags>
    </table>
</lift:surround>

Создаем сниппет app.example.cpuflags.snippet.CpuInfo.scala:
package app.example.cpuflags.snippet

import xml.NodeSeq
import net.liftweb.util.BindHelpers._
import net.liftweb.http.{S}
import java.util.Properties

class CpuInfo {
  def flags(html: NodeSeq): NodeSeq = {
    val a1 = CpuFlags.getInfo(S.param("flags").openOr(""))

    a1.flatMap( p => bind("flag", html,
      "name" -> p._1,
      "description" -> p._2) )
  }
}

object CpuFlags {
  val propertyMap = {
    val props = new Properties
    props.load(this.getClass.getClassLoader.getResourceAsStream("cpuflags.properties"))
    props
  }

  def getInfo(flags: String) = {
    val s = flags.trim.toUpperCase
    if (s.size>0)
      s.split(" +").toList.map( (flag) => (flag, if (propertyMap.getProperty(flag)!=null) propertyMap.getProperty(flag) else "UNKNOWN FLAG") )
    else
      List()
  }
}

cpuflags.properties я создал, используя информацию отсюда. Список не полный, но заморачиваться и искать дальше было лень.

Все, перезапускаем jetty:run и радуемся:


Полезные ссылки
Теги:
Хабы:
+15
Комментарии 16
Комментарии Комментарии 16

Публикации

Истории

Ближайшие события

PG Bootcamp 2024
Дата 16 апреля
Время 09:30 – 21:00
Место
Минск Онлайн
EvaConf 2024
Дата 16 апреля
Время 11:00 – 16:00
Место
Москва Онлайн
Weekend Offer в AliExpress
Дата 20 – 21 апреля
Время 10:00 – 20:00
Место
Онлайн