Download - Scala and LiftWeb presentation (Russian)
Дмитрий Стропалов, Александр Михальчук
+
If I were to pick a language to use today other than Java, it would be Scala
James Gosling
I can honestly say if someone had shown me the Programming in Scala book by by Martin
Odersky, Lex Spoon & Bill Venners back in 2003 I’d probably have never created Groovy
James Strachan
возникновение
• Разработан в 20012004 годах (представлен в 2004 г.) в Лаборатории методов программирования EPFL (Федеральная политехническая школа Лозанны, Швейцария) под руководством Мартина Одерски (Martin Odersky)
• Большое влияние оказали исследовательские работы в области разработки (компонентного) ПО, ЯП
• Попытка разработки языка, который бы хорошо масштабировался (SCAlable LAnguage)
• Унификация объектноориентированного и функционального подходов и предоставление их в языке со статической типизацией. Расширенные механизмы абстракции, композиции и декомпозиции
• Хорошая интеграция с существующими платформами и библиотеками — изначальная ориентация на совместную работу с Java и .NET
«академический» язык
• LinkedIn – фреймфорк “Norbert” для построения асинхронных, кластерных, высоконагруженных приложений. Представление социального графа
• EDF – крупнейшая во Франции энергетическая компания
• Twitter – главная очередь сообщений (message queue)
• Novell – сервис “Vibe Cloud”, предоставляющий корпоративную закрытую и защищенную платформу для совместной работы
• The Guardian – API для “Open Platform”, предоставление доступа к медийному архиву агенства
• Xerox – различное ПО, включая и клиентское
• FourSquare – полностью все предоставляемые сервисы
• Sony – система управления хранением данных
• Siemes (+ SAP) – корпоративная система сообщений
• …
основные концепции
• Бесшовная интеграция с Javaкодом (C#/.NET)
• Единообразная объектная модель – любое значение является объектом, любая операция – вызов метода
• Функциональный язык – функции являются полноправными значениями. Каждая функция возвращает значение
• Механизмы абстракций как для типов, так и для значений
• Использование «примесей» (mixin, trait) для композиции классов
• Сравнение с образцом (pattern matching)
• Естественная обработка документов XML
• Выведение типов
• Неизменяемые (immutable types) типы
• Ленивые (lazy) вычисления
hello world!
Вариант I:object Application {
def main(args: Array[String]): Unit = { for (arg < args) { if (arg.startsWith("")) println(arg) } }
}
hello world!
Вариант I:object Application {
def main(args: Array[String]): Unit = { for (arg < args) { if (arg.startsWith("")) println(arg) } }
}
Вариант II: def main(args: Array[String]): Unit = { for (arg < args; if arg.startsWith("")) println(arg) }
hello world!
Вариант I:object Application {
def main(args: Array[String]): Unit = { for (arg < args) { if (arg.startsWith("")) println(arg) } }
}
Вариант II: def main(args: Array[String]): Unit = { for (arg < args; if arg.startsWith("")) println(arg) }
Вариант III: def main(args: Array[String]): Unit = {
args.withFilter(_.startsWith("")).map(println) }
значения и переменные
val a: Int = 5 val b = { println("Init b") 10 } var c = 15 lazy val d = { println("Init d") a + c }
println("a:%d b:%d c:%d".format(a, b, c)) c = 20 println("c:%d d:%d".format(c, d)) println("d:" + d)
>>> Init b a:5 b:10 c:15 Init d c:20 d:25 d:25
классы и объекты
class Parent(value: String) { val classValue = value
}
class A1 { val a: Int = 0 private var b = 0 def getB = b
}
object A1 extends Parent("Object A1: ") { def setA(obj: A1) { obj.b = 5 }
}
object Application extends Application { val obj = new A1 A1.setA(obj) println(A1.classValue + obj.getB)
} >>> Object A1: 5
трейты (traits)
trait Role { def getRole: Int def isAdmin = if (getRole == 0) true else false
}
trait Info { def getInfo: String def setInfo(info: String)
}
class User(role: Int) extends Role with Info { val userRole = role var userInfo: String = "" def getRole = userRole def getInfo = userInfo def setInfo(info: String) {
userInfo = info }
}
анонимные функции
object Application extends Application { val hello = () => "Hello, anonymous" val foo = (x: String) => x.split(':').mkString(", ") def bar(s: String, f: String => String) = { f(s) }
println(hello)println(hello())
println(foo("value1:value2:value3:value4")) println(bar("John:Mike:Ann", foo))
}
>>> <function0>Hello, anonymousvalue1, value2, value3, value4John, Mike, Ann
карринг
def filter(xs: List[Int], p: Int => Boolean): List[Int] =if (xs.isEmpty) Nilelse if (p(xs.head)) xs.head :: filter(xs.tail, p)else filter(xs.tail, p)
def modN(n: Int)(x: Int) = ((x % n) == 0)
val list = (1 to 10).toListprintln(filter(list, modN(2)))println(filter(list, modN(3)))
>>> List(2, 4, 6, 8, 10)List(3, 6, 9)
xml
val xml = <depot> <item name="item 1"> <part name="part 1"/> <part name="part 2"/> <item>inner item</item> </item> <item name="item 2"></item> <label type="title">Depot title</label> </depot>
xml \ "item" xml \\ "item" xml \\ "@type"
xml
val ptype = "box" val title = "Examples" val item = <item type={ ptype }>{ title }</item>
val items = <items> {for (index < (0 to 3).toList) yield <item>{ index }</item>} </items>
println(item) println(items)
>>> <item type="box">Examples</item><items>
<item>0</item><item>1</item><item>2</item><item>3</item>
</items>
сравнение с образцом
def matcher(x: Any) = { x match { case 1 => println("Integer value 1") case y: Int => println("Integer value: " + y) case <e>{ tagValue }</e> =>
println("XML Tag value: " + tagValue) case _ => println("Something else: " + x) } }
matcher(1) matcher(10) matcher(<e>Lorem ipsum</e>) matcher("simple string")
>>> Integer value 1Integer value: 10XML Tag value: Lorem ipsumSomething else: simple string
магический implicit
implicit def double2int(x: Double) = { println("converting double to int ...") if (x < 10) 0 else x.toInt } val i: Int = 3.5 println(i)
>>> converting double to int ...0
implicit val a: Int = 10 implicit val b: Double = 1.5 def foo(implicit a: Int, b: Double) = {
println("%d, %f".format(a, b))}
foo
>>> 10, 1.500000
The first rule of Lift is: It's easier than you think.
I think the most important feature we have in Lift is the community around it
(committers and noncommitters). I have joined the mailing list just a few months
ago and I am impressed with how willing everyone is to help each other.
общая информация
• Разработка начата в 2007 году, сейчас достигнута версия 2.2. Главный разработчик — Девид Поллак (David Pollak). Рабочее название было “Scala on Sails”
• Разрабатывается с целью объединить лучшие идеи и концепции из существующих webфреймворков (Javaфреймворки, RoR)
• Концентрация на бизнеслогике приложения
• Простая и эффективная система безопасности
• Использование преимуществ Scala как базового языка (контроль типов, встроенная обработка XML, функциональные элементы)
• ViewFirst предпочтительнее MVCмодели
• Изначальная поддержка AJAX и Comet
• Используется в таких компаниях, как Nowell, SAP, Innovation Games, Foursquare, Untyped и другими
сообщество
• Домашняя страница: http://liftweb.net/
• Google Groups (2500 участников): http://groups.google.com/group/liftweb
• Открытый исходный код на GitHub: http://github.com/dpp/liftweb/tree/master
• Wiki и треккер на Assembla: http://www.assembla.com/wiki/show/liftweb/
• Бесплатные книги:
Exploring Lift: http://exploring.liftweb.net/
Simply Lift: http://stable.simply.liftweb.net/
• Дружелюбное комьюнити и Поллак
быстрый старт
• Генерация приложения Scala 2.8.1 + Lift 2.2:
mvn archetype:generate \
DarchetypeGroupId=net.liftweb \
DarchetypeArtifactId=liftarchetypebasic_2.8.1 \
DarchetypeVersion=2.2 \
DarchetypeRepository=http://scalatools.org/reporeleases \
DremoteRepositories=http://scalatools.org/reporeleases \
DgroupId=ua.dn.cnc \
DartifactId=lift_demo \
Dversion=1.0
• Запуск сервера:
mvn jetty:run
структура проекта
Структура каталога нового проекта:
• project – Simple Build Tool (SBT) конфигурация
• src/main/resources – ресурсы проекта
• src/main/scala – исходный код на Scala
• ua.dn.cnc.Boot – конфигурация Lift приложения
• ua.dn.cnc.lib.DependencyFactory – Dependency Injection
• ua.dn.cnc.model.User – сущность User
• ua.dn.cnc.snippet.HelloWorld – Hello World сниппет
• src/main/webapp – web ресурсы (шаблоны, html, css, js)
• src/main/test исходный код тестов
• pom.xml – Apache Maven конфигурация
mapper orm
object News extends News with LongKeyedMetaMapper[News] { override def dbIndexes =
UniqueIndex(title) :: super.dbIndexes}class News extends LongKeyedMapper[News] with IdPK { def getSingleton = News
object title extends MappedString(this, 255) { override def dbNotNull_? = true } object text extends MappedString(this, 1200) { override def dbNotNull_? = true } object publish extends MappedBoolean(this) { override def dbNotNull_? = true override def defaultValue: Boolean = true } object date extends MappedDate(this) { override def dbNotNull_? = true }}
mapper orm
Создание записи:val n = News.createn.title("Lorem ipsum")…n.save
Удаление записи:n.delete_!
Все записи:val n = News.findAll
Записи по условию: val n = News.findAll(By(News.publish, true), OrderBy(News.date, Descending), StartAt(0), MaxRows(10))
Записи по SQLзапросу: val n = News.findAllByInsecureSql("select * from news " + "where publish = 1 order by(date_c)", IHaveValidatedThisSQL("dba", "20110101"))
menu
Boot.scala:
import net.liftweb.http.LiftRulesimport net.liftweb.sitemap.Locimport net.liftweb.sitemap.Loc.Hiddenimport net.liftweb.sitemap.Loc.strLstToLinkimport net.liftweb.sitemap.Menuimport net.liftweb.sitemap.SiteMap...def boot {
...val myLoc = Loc("HomePage", "index" :: Nil, "Home Page",
Hidden)val myMenu = Menu(myLoc)val allMenus = myMenu :: User.sitemapval mySiteMap = SiteMap(allMenus: _*)LiftRules.setSiteMap(mySiteMap)...
menu
Loc:Loc(theName:"Home", theLink:new Link(List("index")),
theText:"Home Page")Loc("About", "info" :: "about" :: Nil, "About", Hidden)Loc("HomePage", "index" :: Nil, "Home Page", Test((req) =>
req.isIE6))Loc("Static", Link(List("static"), true, "/static/index"),
"Static Content")Menu:
val myMenu = Menu(Loc("About", "about" :: Nil, "About", Hidden))
val helpMenu = Menu(myHelpMenuLoc, myUserHelpMenu, myTechnicalHelpMenu)
val allMenus = myMenu :: User.menus
SiteMap:LiftRules.setSiteMap(SiteMap(allMenus: _*))
HTMLвывод:<lift:Menu.builder /> или <div class="lift:Menu.builder" />
url rewriting
bootstrap.liftweb.Boot.boot:
LiftRules.rewrite.prepend(NamedPF("ArticleRewrite") {case RewriteRequest(ParsePath("article" :: id :: action :: _, _, _, _)
, _, _) =>RewriteResponse(
"article" :: action :: Nil, Map("id" > id))})
/article/1/edit > /article/edit?id=1
шаблоны
<lift:surround with="default">
<lift:bind-at name="controls"><head>
<script src="…"/> </head><div class="controls">
…</div>
</lift:bind-at>
<lift:bind-at name="content"><div class="content">
…</div>
</lift:bind-at>
</lift:surround>
index.htmldefault.html
<html> <head> … </head>
<body> <div> … <lift:bind name="controls" /> </div>
…
<lift:bind name="content" />
… </body></html>
snippets
class Test {def show(in: NodeSeq): NodeSeq = {
TestModel.findAll().flatMap(item => bind("e", in, "item" > Text( "%s: %s".format(item.title, item.text) )) )
}}
… <ul> <lift:Test.show> <li><e:item/></li> </lift:Test.show> </ul>…
… <ul> <li>Foo: Bar</li> <li>Fizz: Buzz</li> <li>Some title: and some text</li> </ul>…
формы
Автоматическая генерация:Model.toForm(Full("Save"), { _.save })
Snippet:private var newKeyword = ""
def keywordsForm(in: NodeSeq): NodeSeq = { bind("entry", in, "addNewKeyword" > SHtml.text("", newKeyword = _,
"maxlength" > "50"), "submitNewKeyword" > SHtml.submit("Добавить",
addNewKeyword)) }
private def addNewKeyword() { val kw =
KeywordsModel.create kw.keyword(newKeyword) kw.save
}
… <lift:clazz.keywordsForm form="POST"> <entry:addNewKeyword /> <entry:submitNewKeyword /> </lift:clazz.keywordsForm>…
ссылки
• Обзор языка программирования Scala
• Статьи на Habrahabr
• Programming Scala
• Scala Reference Manuals
• Scala Style Guide
• Exploring Lift
• Lift Wiki
Спасибо за внимание и EOF