Das Web-Framework für Scala
     Heiko Seeberger



        Copyright WeigleWilczek 2009
WARUM SIND WIR EIGENTLICH HIER?

 “Lift is the only new framework in the last four years to offer fresh and
      innovative approaches to web development. It's not just some
incremental improvements over the status quo, it redefines the state of
        the art. If you are a web developer, you should learn Lift ...”

                                                   Michael Galpin, Developer, eBay




                                    2
WARUM NOCH EIN WEB-FRAMEWORK?
                           Lift pickt Rosinen von
                           anderen Frameworks




  Lift bringt eigene
innovative Ansätze

                       3
DEMO: CHAT MIT 20 ZEILEN SCALA CODE




                 4
FÜR’S PROTOKOLL

•   Projekt generieren mit mvn archetype:generate ...

•   ChatServer.scala anlegen

•   Chat.scala anlegen

•   chat.xhtml Template anlegen

•   In Boot.scala neues Menu zur SiteMap hinzufügen


                                     5
DIE ANFÄNGE
     David Pollak                  Einfach zu benutzen




  Scala with Sails                 Sicher
                        2006

Keep the meaning
   with the bytes                  Einfach zu deployen

                          6
HEUTE
Lift 1.1 vor der Tür                Sehr rege Community




Etliche Sites powered by Lift             32 Committer

                                7
FEATURES IM ANGEBOT

 Templates         Sitemap



Comet                  Persistenz



        AJAX       User Management


               8
“MUSTERLÖSUNG”




      9
WEITERE FEATURES (UNVOLLSTÄNDIG)

        Wizards         JPA und JTA

    OpenID                   JSON


     AMPQ                    OSGi

         Textile        PayPay


                   10
TEMPLATES




    11
WIE SCHREIBEN WIR TEMPLATES?

        <html>
          <head>
                                                   In XHTML
            <title>kix</title>
            ...
          <body>
            <div class="container">
               ...
               <div class="span-18 last">
                 <lift:Msgs showAll="true"/>
mit Lift-Tags    <lift:bind name="content"/>
                 ...
               </div>
               <div class="span-12" style="text-align: center;">
                 <img src="/images/pbww.png"/>
               </div>
               ...




                                   12
TEMPLATES VERSCHACHTELN

  <lift:bind ... />




            <lift:surround ...>




            13
SNIPPETS EINBINDEN
                   class Games {
        Snippet      def upcoming5(xhtml: NodeSeq) =
                       bind("games", xhtml, "list" -> bindGames(Game upcoming 5, xhtml))




<lift:surround with="default" at="content">
      ...
      <lift:Games.upcoming5> Snippet-Tag
        <games:list>
          <tr game:class="">
            <td><span game:id=""><game:action/></span></td>
            <td><game:date/></td>
            <td><game:group/></td>              Platzhalter im
            <td><game:location/></td>
            <td><game:teams/></td>              Namespace game
            ...




                                           14
SNIPPETS UND BINDING

class Games {

  def upcoming5(xhtml: NodeSeq) =
    bind("games", xhtml, "list" -> bindGames(Game upcoming 5, xhtml))

  private def bindGames(games: List[Game], xhtml: NodeSeq) = {
    val oddOrEven = OddOrEven()
    games flatMap { game =>
      bind("game", chooseTemplate("games", "list", xhtml),
           "date" -> format(game.date.is, locale),
           "group" -> game.group.is.toString,
           "location" -> game.location.is,
           ...
                        Platzhalter ersetzen


                                 15
LIVE DEMO




    16
SITEMAP




   17
MENÜS UND ZUGRIFFSKONTROLLE




             18
SITEMAP ANLEGEN

val ifAdmin = If(() => User.superUser_?, () => RedirectResponse("/index"))

val homeMenu = Menu(Loc("home", ("index" :: Nil) -> false, "Home"))
...
val adminMenu = Menu(Loc("admin", ("admin" :: Nil) -> true, "Admin", ifAdmin),
                     adminSubMenu: _*)

val menus = homeMenu ::
            ...
            adminMenu ::
            User.sitemap

LiftRules setSiteMap SiteMap(menus : _*)




                                       19
MENÜS ANZEIGEN

<html>
  <head>
    <title>kix</title>
    ...
  <body>
    <div class="container">
       ...
       <div class="span-5">
         <div class="menu">
           <lift:Menu.builder/>
         </div>
         ...




                            20
LIVE DEMO




    21
PERSISTENZ




    22
MAPPER
class Game extends LongKeyedMapper[Game] with IdPK {

  object group extends MappedEnum(this, Group) {
    override def displayName = ?("Group")
  }

  object team1 extends MappedTeam(this, "Team 1")

  object team2 extends MappedTeam(this, "Team 2")

  object date extends MappedDateTime(this) {
    override def displayName = ?("Date")
  }

  object location extends MappedString(this, 100) {
    override def displayName = ?("Location")
  }
  ...                                          Keep the meaning
                                               with the bytes
                         23
CRUD SUPPORT

... with CRUDify[Long, Game]




        24
SCHEMA ANLEGEN


val dbVendor =
  new StandardDBVendor(Props get "db.driver" openOr "org.h2.Driver",
                       Props get "db.url" openOr "jdbc:h2:kix",
                       Empty, Empty) {
  override def maxPoolSize = Props getInt "db.pool.size" openOr 3
}
DB.defineConnectionManager(DefaultConnectionIdentifier, dbVendor)
Schemifier.schemify(true, Log.infoF _, Team, Game, Result, Tip, User)




                                 25
LIVE DEMO




    26
USER MANAGEMENT




       27
CLAUSTHALER-PRINZIP




         28
MORE THAN JUST CRUD

class User extends MegaProtoUser[User] {

    override def shortName = {
      val s = super.shortName
      val i = s indexOf "@"
      if (i == -1) s else s.substring(0, i)
    }

    override def firstNameDisplayName = ?("Name")

    object points extends MappedInt(this)
}




                                   29
LIVE DEMO




    30
AJAX SUPPORT




     31
BEISPIEL: LISTEN-ELEMENTE LÖSCHEN




                32
SCALA API FÜR AJAX

def editDelete(tip: Tip) = {
  def delete = {
    Tip delete_! tip
    SetHtml(tip.id.is.toString, NodeSeq.Empty)
  }
  renderEditDelete(tip, delete _)
}

private def renderEditDelete(tip: Tip, jsCmd: () => JsCmd) =
  link("/tips/edit", () => currentTip(Full(tip)), editImg) ++
  Text("") ++
  ajaxDeleteImg(ajaxInvoke(jsCmd))




                                 33
LIVE DEMO




    34
COMET SUPPORT




      35
CHATTEN LEICHT GEMACHT




          36
FRAGEN / DISKUSSION




         37
DANKE

Kontakt: seeberger@weiglewilczek.com

 Mehr lernen: www.scalatraining.net




                 38

W-JAX 09 - Lift

  • 1.
    Das Web-Framework fürScala Heiko Seeberger Copyright WeigleWilczek 2009
  • 2.
    WARUM SIND WIREIGENTLICH HIER? “Lift is the only new framework in the last four years to offer fresh and innovative approaches to web development. It's not just some incremental improvements over the status quo, it redefines the state of the art. If you are a web developer, you should learn Lift ...” Michael Galpin, Developer, eBay 2
  • 3.
    WARUM NOCH EINWEB-FRAMEWORK? Lift pickt Rosinen von anderen Frameworks Lift bringt eigene innovative Ansätze 3
  • 4.
    DEMO: CHAT MIT20 ZEILEN SCALA CODE 4
  • 5.
    FÜR’S PROTOKOLL • Projekt generieren mit mvn archetype:generate ... • ChatServer.scala anlegen • Chat.scala anlegen • chat.xhtml Template anlegen • In Boot.scala neues Menu zur SiteMap hinzufügen 5
  • 6.
    DIE ANFÄNGE David Pollak Einfach zu benutzen Scala with Sails Sicher 2006 Keep the meaning with the bytes Einfach zu deployen 6
  • 7.
    HEUTE Lift 1.1 vorder Tür Sehr rege Community Etliche Sites powered by Lift 32 Committer 7
  • 8.
    FEATURES IM ANGEBOT Templates Sitemap Comet Persistenz AJAX User Management 8
  • 9.
  • 10.
    WEITERE FEATURES (UNVOLLSTÄNDIG) Wizards JPA und JTA OpenID JSON AMPQ OSGi Textile PayPay 10
  • 11.
  • 12.
    WIE SCHREIBEN WIRTEMPLATES? <html> <head> In XHTML <title>kix</title> ... <body> <div class="container"> ... <div class="span-18 last"> <lift:Msgs showAll="true"/> mit Lift-Tags <lift:bind name="content"/> ... </div> <div class="span-12" style="text-align: center;"> <img src="/images/pbww.png"/> </div> ... 12
  • 13.
    TEMPLATES VERSCHACHTELN <lift:bind ... /> <lift:surround ...> 13
  • 14.
    SNIPPETS EINBINDEN class Games { Snippet def upcoming5(xhtml: NodeSeq) = bind("games", xhtml, "list" -> bindGames(Game upcoming 5, xhtml)) <lift:surround with="default" at="content"> ... <lift:Games.upcoming5> Snippet-Tag <games:list> <tr game:class=""> <td><span game:id=""><game:action/></span></td> <td><game:date/></td> <td><game:group/></td> Platzhalter im <td><game:location/></td> <td><game:teams/></td> Namespace game ... 14
  • 15.
    SNIPPETS UND BINDING classGames { def upcoming5(xhtml: NodeSeq) = bind("games", xhtml, "list" -> bindGames(Game upcoming 5, xhtml)) private def bindGames(games: List[Game], xhtml: NodeSeq) = { val oddOrEven = OddOrEven() games flatMap { game => bind("game", chooseTemplate("games", "list", xhtml), "date" -> format(game.date.is, locale), "group" -> game.group.is.toString, "location" -> game.location.is, ... Platzhalter ersetzen 15
  • 16.
  • 17.
  • 18.
  • 19.
    SITEMAP ANLEGEN val ifAdmin= If(() => User.superUser_?, () => RedirectResponse("/index")) val homeMenu = Menu(Loc("home", ("index" :: Nil) -> false, "Home")) ... val adminMenu = Menu(Loc("admin", ("admin" :: Nil) -> true, "Admin", ifAdmin), adminSubMenu: _*) val menus = homeMenu :: ... adminMenu :: User.sitemap LiftRules setSiteMap SiteMap(menus : _*) 19
  • 20.
    MENÜS ANZEIGEN <html> <head> <title>kix</title> ... <body> <div class="container"> ... <div class="span-5"> <div class="menu"> <lift:Menu.builder/> </div> ... 20
  • 21.
  • 22.
  • 23.
    MAPPER class Game extendsLongKeyedMapper[Game] with IdPK { object group extends MappedEnum(this, Group) { override def displayName = ?("Group") } object team1 extends MappedTeam(this, "Team 1") object team2 extends MappedTeam(this, "Team 2") object date extends MappedDateTime(this) { override def displayName = ?("Date") } object location extends MappedString(this, 100) { override def displayName = ?("Location") } ... Keep the meaning with the bytes 23
  • 24.
    CRUD SUPPORT ... withCRUDify[Long, Game] 24
  • 25.
    SCHEMA ANLEGEN val dbVendor= new StandardDBVendor(Props get "db.driver" openOr "org.h2.Driver", Props get "db.url" openOr "jdbc:h2:kix", Empty, Empty) { override def maxPoolSize = Props getInt "db.pool.size" openOr 3 } DB.defineConnectionManager(DefaultConnectionIdentifier, dbVendor) Schemifier.schemify(true, Log.infoF _, Team, Game, Result, Tip, User) 25
  • 26.
  • 27.
  • 28.
  • 29.
    MORE THAN JUSTCRUD class User extends MegaProtoUser[User] { override def shortName = { val s = super.shortName val i = s indexOf "@" if (i == -1) s else s.substring(0, i) } override def firstNameDisplayName = ?("Name") object points extends MappedInt(this) } 29
  • 30.
  • 31.
  • 32.
  • 33.
    SCALA API FÜRAJAX def editDelete(tip: Tip) = { def delete = { Tip delete_! tip SetHtml(tip.id.is.toString, NodeSeq.Empty) } renderEditDelete(tip, delete _) } private def renderEditDelete(tip: Tip, jsCmd: () => JsCmd) = link("/tips/edit", () => currentTip(Full(tip)), editImg) ++ Text("") ++ ajaxDeleteImg(ajaxInvoke(jsCmd)) 33
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
    DANKE Kontakt: seeberger@weiglewilczek.com Mehrlernen: www.scalatraining.net 38