Simply Lift by David Pollak - HTML preview

PLEASE NOTE: This is an HTML preview only and some elements such as links or page numbers may be incorrect.
Download the book in PDF, ePub, Kindle for a complete version.

Simply Lift

David Pollak

September 8, 2011

ii

Copyright © 2010-2011 by David Pollak

This document is licensed Creative Commons Attribution, Non Commercial, No Derivatives:

http://creativecommons.org/licenses/by-nc-nd/3.0/

Contents

Contents

iii

List of Figures

v

List of Listings

vii

I

The Lift Web Framework

1

1

Introduction

3

2

The ubiquitous Chat app

5

2.1

The View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

5

2.2

The Chat Comet component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

6

2.3

The ChatServer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

7

2.4

User Input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

8

2.5

Chat In . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

9

2.6

Running it . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

2.7

What you don’t see . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

3

Snippets and SiteMap

11

3.1

Starting at the beginning: Boot.scala . . . . . . . . . . . . . . . . . . . . . . . . . . 11

3.1.1

LiftRules rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

3.1.2

Properties and Run modes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

3.1.3

By convention . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

3.1.4

Misc Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

3.1.5

Html5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

3.2

SiteMap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

3.2.1

Defining the SiteMap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

iii

iv

CONTENTS

3.2.2

Simplest SiteMap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

3.2.3

Menu and Loc[_] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

3.2.4

Access Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

3.2.5

Hidden and Group

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

3.2.6

Submenus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

3.2.7

Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

3.2.8

Wildcards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

3.2.9

Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

3.3

View First . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

3.3.1

Page source . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

3.3.2

Dynamic content . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

3.3.3

Surround and page chrome . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

3.3.4

Embed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

3.3.5

Results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

3.4

Snippets and Dynamic content . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

3.4.1

Snippets in markup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

3.4.2

Snippet resolution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

3.4.3

Dynamic Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

3.4.4

Embedded Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

3.4.5

Param Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

3.4.6

Recursive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

3.4.7

Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

3.5

Wrap up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

4

Forms

27

4.1

Old Fashioned Dumb Forms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

4.2

OnSubmit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

4.3

Stateful Snippets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

4.4

RequestVars . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

4.5

Field Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

4.6

LiftScreen

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

4.7

Wizard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

4.8

Ajax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

4.9

But sometimes Old Fashioned is good . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

4.10 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

CONTENTS

v

5

HTTP and REST

43

5.1

Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

5.2

REST the hard way . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

5.3

Making it easier with RestHelper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

5.4

A complete REST example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57

5.5

Wrap Up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

6

Wiring

63

6.1

Cells . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63

6.2

Hooking it up to the UI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64

6.3

Shared Shopping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

6.4

Wrap up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76

7

Core Concepts

77

7.1

Snippets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78

7.1.1

Snippet NodeSeq => NodeSeq . . . . . . . . . . . . . . . . . . . . . . . . . . 78

7.1.2

Snippet instances . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79

7.1.3

Multiple methods on a snippet class . . . . . . . . . . . . . . . . . . . . . . . . 79

7.1.4

Inter-snippet communication . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79

7.1.5

Recursive Snippets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79

7.1.6

Snippet parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79

7.2

Box/Option . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80

7.3

S/SHtml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84

7.4

Boot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84

7.5

SiteMap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84

7.6

GUIDs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84

7.6.1

How GUIDs are generated . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84

7.6.2

Where they are used . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84

7.7

LiftRules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84

7.8

SessionVars and RequestVars . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84

7.9

Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84

7.10 CSS Selector Transforms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85

7.11 Client-side behavior invoking server-side functions . . . . . . . . . . . . . . . . . . . 89

7.12 Ajax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89

7.13 Comet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89

vi

CONTENTS

7.14 LiftActor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89

7.15 Pattern Matching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89

7.16 Type safety . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89

7.17 Page rewriting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89

7.18 Security . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89

8

Common Patterns

91

8.1

Localization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92

8.1.1

Localizing Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92

8.1.2

Resource Lookup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92

8.1.3

Accessing Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93

8.1.4

Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93

8.2

Dependency Injection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94

8.2.1

Lift Libraries and Injector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94

8.2.2

Lift WebKit and enhanced injection scoping . . . . . . . . . . . . . . . . . . . 95

8.2.3

Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96

8.3

Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96

8.4

HtmlProperties, XHTML and HTML5 . . . . . . . . . . . . . . . . . . . . . . . . . . . 98

8.4.1

XHTML via OldHtmlProperties . . . . . . . . . . . . . . . . . . . . . . . . 98

8.4.2

HTML5 via Html5Properties . . . . . . . . . . . . . . . . . . . . . . . . . . 98

8.4.3

Changing behavior mid-session or mid-request . . . . . . . . . . . . . . . . . 99

9

Built-in Snippets

101

9.1

CSS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

9.2

Msgs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

9.3

Msg . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

9.4

Menu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

9.5

A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

9.6

Children . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

9.7

Comet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

9.8

Form . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

9.9

Ignore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

9.10 Loc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

9.11 Surround . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

9.12 TestCond . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

CONTENTS

vii

9.13 Embed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

9.14 Tail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

9.15 WithParam . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

9.16 VersionInfo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

9.17 SkipDocType . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

9.18 XmlGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

9.19 LazyLoad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

9.20 WithResourceId . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

10 SiteMap

103

11 REST

105

12 MVC (If you really want it)

109

13 From MVC

111

13.1 First things first . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111

13.2 Making a SiteMap entry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112

13.3 Creating the view . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112

13.4 Creating the Snippet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113

13.5 Getting Ajaxy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113

13.6 Next Steps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114

II

Recipes

115

14 Dynamic html tables created from DB.runQuery()

117

14.1 Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117

14.2 Solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117

15 Dynamically choosing content

119

15.1 Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119

15.2 Solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119

16 Ajax Forms

121

17 Protecting REST APIs

123

17.1 Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123

17.2 Solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123

viii

CONTENTS

18 URI-based locale selection

125

18.1 Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125

18.2 Solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125

19 Embedding JavaScript in an HTML page

127

19.1 Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127

19.2 Solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127

III

Questions and Answers

129

20 Scaling

131

21 How Lift does function/GUID mapping

137

22 How Lift does Comet

139

23 Advanced Concepts

141

23.1 Snippet Resolution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141

23.1.1 LiftSession.liftTagProcessing . . . . . . . . . . . . . . . . . . . . . . 142

23.1.2 LiftRules.liftTagProcessing . . . . . . . . . . . . . . . . . . . . . . . . 142

23.1.3 Snippet name resolution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142

23.1.4 Post-processing of results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144

23.2 The Merging Phase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144

IV

Misc

145

24 Releases

147

24.1 Lift 2.2-RC1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148

24.2 Lift 2.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150

List of Figures

ix

x

LIST OF FIGURES

List of Listings

2.1

index.html . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

5

2.2

Chat.scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

6

2.3

ChatServer.scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

7

2.4

ChatIn.scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

9

3.1

Boot.scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

3.2

index.html . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

3.3

dynamic.html . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

3.4

HelloWorld.scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

samples/snippet_and_sitemap/src/main/webapp/_embedme.html . . . . . . . . . . . . 22

3.5

Embedded.scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

3.6

param.html . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

3.7

Param.scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

3.8

recurse.html . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

3.9

Recurse.scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

4.1

dumb.html . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

4.2

DumbForm.scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

4.3

onsubmit.html . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

4.4

OnSubmit.scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

4.5

stateful.html . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

4.6

Stateful.scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

4.7

requestvar.html . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

4.8

ReqVar.scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

4.9

fielderror.html . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

4.10 FieldErrorExample.scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

4.11 screen.html . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

4.12 ScreenExample.scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

xi

xii

LIST OF LISTINGS

4.13 WizardExample.scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

4.14 ajax.html . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

4.15 AjaxExample.scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

4.16 Query.scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

5.1

BasicExample.scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44

5.2

Item.scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46

5.3

BasicWithHelper.scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

5.4

FullRest.scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57

6.1

Cart.scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

6.2

AllItemsPage.scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68

6.3

items.html . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69

6.4

AnItemPage.scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69

6.5

CometCart.scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70

6.6

Link.scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74

6.7

ShareCart.scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75

13.1 index.html . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112

13.2 TimeNow.scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113

13.3 ClickMe.scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113

Part I

The Lift Web Framework

1

Chapter 1

Introduction

The Lift Web Framework provides web application developers tools to make writing security, in-

teracting, scalable web applications easier than with any other web framework. After reading Part

I of this book, you should understand Lift’s core concepts and be able to write Lift applications.

But with anything, practice is important. I have been writing Lift and Scala for 4 years, and even

I learn new things about the language and the framework on a weekly basis. Please consider Lift

an path and an exploration, rather than an end point.

“Yo, David, stop yer yappin’. I’m coming from Rails|Spring|Struts|Django and I want to get

started super fast with Lift.” See From MVC ( 13 on page 111).

Lift is built on top of the Scala programming language. Scala runs on the Java Virtual Machine. Lift applications are typically packaged as WAR files and run as a J/EE Servlets or Servlet Filters. This book will provide you with the core concepts you need to successfully write Lift web applications.

The book assumes knowledge of Servlets and Servlet containers, the Scala Language (Chapters 1-6

of Beginning Scala gives you a good grounding in the language), build tools, program editors, web development including HTML and JavaScript, etc. Further, this book will not explore persistence.

Lift has additional modules for persisting to relational and non-relational data stores. Lift doesn’t

distinguish as to how an object is materialized into the address space... Lift can treat any object

any old way you want. There are many resources (including Exploring Lift) that cover ways to persist data from a JVM.

Lift is different from most web frameworks and it’s likely that Lift’s differences will present a

challenge and a friction if you are familiar with the MVC school of web frameworks1. But Lift

is different and Lift’s differences give you more power to create interactive applications. Lift’s

differences lead to more concise web applications. Lift’s differences result in more secure and

scalable applications. Lift’s differences let you be more productive and make maintaining appli-

cations easier for the future you or whoever is writing your applications. Please relax and work to

understand Lift’s differences... and see how you can make best use of Lift’s features to build your

web applications.

Lift creates abstractions that allow easier expression of business logic and then maps those ab-

stractions to HTTP and HTML. This approach differs from traditional web frameworks which

build abstractions on top of HTTP and HTML and require the developer to bridge between com-

mon business logic patterns and the underlying protocol. The difference means that you spend

more time thinking about your application and less time thinking about the plumbing.

1This includes Ruby on Rails, Struts, Java Server Faces, Django, TurboGears, etc.

3

4

CHAPTER 1. INTRODUCTION

I am a “concept learner.” I learn concepts and then apply them over and over again as situations

come up. This book focuses a lot on the concepts. If you’re a concept learner and like my stream

on conciousness style, this book will likely suit you well. On the other hand, it may not.

Up to date versions of this book are available in PDF form at http://simply.

liftweb.net/Simply_Lift.pdf.

The source code for this book is available at

https://github.com/dpp/simply_lift.

If you’ve got questions, feedback, or improvements to this document, please join the conversation

on the Lift Google Group.

I’m a “roll up your sleaves and get your hands dirty with code” kinda guy... so let’s build a simple

Chat application in Lift. This application will allow us to demonstrate some of Lift’s core features

as well as giving a “smack in the face” demonstration of how Lift is different.

Chapter 2

The ubiquitous Chat app

Writing a multi-user chat application in Lift is super-simple and illustrates many of Lift’s core

concepts.

The Source Code can be found at https://github.com/dpp/simply_lift/tree/master/chat.

2.1

The View

When writing a Lift app, it’s often best to start off with the user interface... build what the user

will see and then add behavior to the HTML page. So, let’s look at the Lift template that will make

up our chat application.

Listing 2.1: index.html

1

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

2

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

3

<html xmlns="http://www.w3.org/1999/xhtml">

4

<head><title>Home</title></head>

5

<body class="lift:content_id=main">

6

<div id="main" class="lift:surround?with=default;at=content">

7

<!-- the behavior of the div -->

8

<div class="lift:comet?type=Chat">

9

Some chat messages

10

<ul>

11

<li>A message</li>

12

<li class="clearable">Another message</li>

13

<li class="clearable">A third message</li>

14

</ul>

15

</div>

16

17

<div>

18

<form class="lift:form.ajax">

19

<input class="lift:ChatIn" id="chat_in"/>

20

<input type="submit" value="Say Something"/>

21

</form>

22

</div>

23

</div>

5

6

CHAPTER 2. THE UBIQUITOUS CHAT APP

24

</body>

25

</html>

It’s a valid HTML page, but there are some hinky looking class attributes. The first one is <body

class="lift:content_id=main">. The class in this case says “the actual page content is

contained by the element with id=’main’.” This allows you to have valid HTML pages for each of

your templates, but dynamically add “chrome” around the content based on one or more chrome

templates.

Let’s

look

at

the

<div id="main">.

It’s

got

a

funky

class

as

well:

lift:surround?with=default;at=content.

This class invokes a snippet which sur-

rounds the <div> with the default template and inserts the <div> and its children at the element

with id “content” in the default template. Or, it wraps the default chrome around the <div>. For

more on snippets, see 7.1 on page 78.

Next, we define how we associate dynamic behavior with the list of chat elements: <div

class="lift:comet?type=Chat">. The “comet” snippet looks for a class named Chat that

extends CometActor and enables the mechanics of pushing content from the CometActor to the

browser when the state of the CometActor changes.

2.2

The Chat Comet component

The Actor Model provides state in functional languages include Erlang. Lift has an Actor library and LiftActors (see 7.14) provides a powerful state and concurrency model. This may all seem

abstract, so let’s look at the Chat class.

Listing 2.2: Chat.scala

1

package code

2

package comet

3

4

import net.liftweb._

5

import http._

6

import util._

7

import Helpers._

8

9

/**

10

* The screen real estate on the browser will be represented

11

* by this component. When the component changes on the server

12

* the changes are automatically reflected in the browser.

13

*/

14

class Chat extends CometActor with CometListener {

15

private var msgs: Vector[String] = Vector() // private state

16

17

/**

18

* When the component is instantiated, register as

19

* a listener with the ChatServer

20

*/

21

def registerWith = ChatServer

22

23

/**

2.3. THE CHATSERVER

7

24

* The CometActor is an Actor, so it processes messages.

25

* In this case, we're listening for Vector[String],

26

* and when we get one, update our private state

27

* and reRender() the component. reRender() will

28

* cause changes to be sent to the browser.

29

*/

30

override def lowPriority = {

31

case v: Vector[String] => msgs = v; reRender()

32

}

33

34

/**

35

* Put the messages in the li elements and clear

36

* any elements that have the clearable class.

37

*/

38

def render = "li *" #> msgs & ClearClearable

39

}

The Chat component has private state, registers with the ChatServer, handles incoming mes-

sages and can render itself. Let’s look at each of those pieces.

The private state, like any private state in prototypical object oriented code, is the state that defines

the object’s behavior.

registerWith is a method that defines what component to register the Chat component with. Reg-

istration is a part of the Listener (or Observer) pattern. We’ll look at the definition of the ChatServer in a minute.

The lowPriority method defines how to process incoming messages. In this case, we’re Pat-

tern Matching (see Section 7.15) the incoming message and if it’s a Vector[String], then we

perform the action of setting our local state to the Vector and re-rendering the component. The

re-rendering will force the changes out to any browser that is displaying the component.

We define how to render the component by defining the CSS to match and the replacement (See

Section 7.10). We match all the <li> tags of the template and for each message, create an <li> tag with the child nodes set to the message. Additionally, we clear all the elements that have the

clearable in the class attribute.

That’s it for the Chat CometActor component.

2.3

The ChatServer

The ChatServer code is:

Listing 2.3: ChatServer.scala

1

package code

2

package comet

3

4

import net.liftweb._

5

import http._

6

import actor._

7

8

/**

8

CHAPTER 2. THE UBIQUITOUS CHAT APP

9

* A singleton that provides chat features to all clients.

10

* It's an Actor so it's thread-safe because only one

11

* message will be processed at once.

12

*/

13

object ChatServer extends LiftActor with ListenerManager {

14

private var msgs = Vector("Welcome") // private state

15

16

/**

17

* When we update the listeners, what message do we send?

18

* We send the msgs, which is an immutable data structure,

19

* so it can be shared with lots of threads without any

20

* danger or locking.

21

*/

22

def createUpdate = msgs

23

24

/**

25

* process messages that are sent to the Actor. In

26

* this case, we're looking for Strings that are sent

27

* to the ChatServer. We append them to our Vector of

28

* messages, and then update all the listeners.

29

*/

30

override def lowPriority = {

31

case s: String => msgs :+= s; updateListeners()

32

}

33

}

The ChatServer is defined as an object rather than a class. This makes it a singleton which

can be referenced by the name ChatServer anywhere in the application. Scala’s singletons differ

from Java’s static in that the singleton is an instance of an object and that instance can be passed

around like any other instance. This is why we can return the ChatServer instance from the

registerWith method in that Chat component.

The ChatServer has private state, a Vector[String] representing the list of chat messages. Note

that Scala’s type inferencer infers the type of msgs so you do not have to explicitly define it.

The createUpdate method generates an update to send to listeners. This update is sent when a

listener registers with the ChatServer or when the updateListeners() method is invoked.

Finally, the lowPriority method defines the messages that this component can handle. If the

ChatServer receives a String as a message, it appends the String to the Vector of messages

and updates listeners.

2.4

User Input

Let’s go back to the view and see how the behavior is defined for adding lines to the chat.

<form class="lift:form.ajax"> defines an input form and the form.ajax snippet turns a

form into an Ajax (see Section 7.12) form that will be submitted back to the server without causing a full page load.

Next, we define the input form element: <input class="lift:ChatIn" id="chat_in"/>.

It’s a plain old input form, but we’ve told Lift to modify the <input>’s behavior by calling the

ChatIn snippet.

2.5. CHAT IN

9

2.5

Chat In

The ChatIn snippet (See Section 7.1) is defined as:

Listing 2.4: ChatIn.scala

1

package code

2

package snippet

3

4

import net.liftweb._

5

import http._

6

import js._

7

import JsCmds._

8

import JE._

9

10

import comet.ChatServer

11

12

/**

13

* A snippet transforms input to output... it transforms

14

* templates to dynamic content. Lift's templates can invoke

15

* snippets and the snippets are resolved in many different

16

* ways including "by convention". The snippet package

17

* has named snippets and those snippets can be classes

18

* that are instantiated when invoked or they can be

19

* objects, singletons. Singletons are useful if there's

20

* no explicit state managed in the snippet.

21

*/

22

object ChatIn {

23

24

/**

25

* The render method in this case returns a function

26

* that transforms NodeSeq => NodeSeq. In this case,

27

* the function transforms a form input element by attaching

28

* behavior to the input. The behavior is to send a message

29

* to the ChatServer and then returns JavaScript which

30

* clears the input.

31

*/

32

def render = SHtml.onSubmit(s => {

33

ChatServer ! s

34

SetValById("chat_in", "")

35

})

36

}

The code is very simple. The snippet is defined as a method that associates a function with form

element submission, onSubmit. When the element is submitted, be that normal form submission,

Ajax, or whatever, the function is applied to the value of the form. In English, when the user

submits the form, the function is called with the user’s input.

The function sends the input as a message to the ChatServer and returns JavaScript that sets the

value of the input box to a blank string.

10

CHAPTER 2. THE UBIQUITOUS CHAT APP

2.6

Running it

Running the application is easy. Make sure you’ve got Java 1.6 or better installed on your machine.

Change directories into the chat directory and type sbt update ~jetty-run. The Simple

Build Tool will download all necessary dependencies, compile the program and run it.

You can point a couple of browsers to http://localhost:8080 and start chatting.

Oh, and for fun, try entering <script>alert(’I ownz your browser’);<script> and

see what happens. You’ll note it’s what you want to happen.

2.7

What you don’t see

Excluding imports and comments, there are about 20 lines of Scala code to implement a multi-

threaded, multi-user chat application. That’s not a lot.

The first thing that’s missing is synchronization or other explicit forms of thread locking. The

application takes advantage of Actors and immutable data structures, thus the developer can focus

on the business logic rather than the threading and locking primatives.

The next thing that’s missing is routing and controllers and other stuff that you might have to do

to wire up Ajax calls and polling for server-side changes (long or otherwise). In our application,

we associated behavior with display and Lift took care of the rest (see Section 7.17).

We didn’t do anything to explicitly to avoid cross-site scripting in our application. Because Lift

takes advantage of Scala’s strong typing and type safety (see Section 7.16), Lift knows the difference between a String that must be HTML encoded and an HTML element that’s already properly

encoded. By default, Lift applications are resistant to many of the OWASP top 10 security vulner-

abilities (see Section 7.18).

This example shows many of Lift’s strengths. Let’s expand the application and see how Lift’s

strengths continue to support the development of the application.

Chapter 3

Snippets and SiteMap

Lift services HTTP request in three ways: generating HTML pages, low level HTTP responses

(e.g., REST), and responding to Ajax/Comet requests. Lift treats each type of request differently

to make the semantics for responding to each type of request most natural. Put another way, it’s

different to build a complex HTML page with lots of different components than to send back some

JSON data that corresponds to a database record.

In this chapter, we’re going to explore how Lift does dynamic HTML page generation based on

the incoming HTTP request and URL including putting “chrome” around the HTML page (menus,

etc.), placing dynamic content on each page, and site navigation including access control.

The code for this chapter can be found in the samples/snippet_and_sitemap directory of the

Simply Lift distribution.

3.1

Starting at the beginning: Boot.scala

When your Lift application first starts up, it executes the code in Boot.scala:

Listing 3.1: Boot.scala

1

package bootstrap.liftweb

2

3

import net.liftweb._

4

import util._

5

import Helpers._

6

7

import common._

8

import http._

9

import sitemap._

10

import Loc._

11

12

import code.snippet._

13

14

/**

15

* A class that's instantiated early and run. It allows the application

16

* to modify lift's environment

17

*/

11

12

CHAPTER 3. SNIPPETS AND SITEMAP

18

class Boot {

19

/**

20

* Calculate if the page should be displayed.

21

* In this case, it will be visible every other minute

22

*/

23

def displaySometimes_? : Boolean =

24

(millis / 1000L / 60L) % 2 == 0

25

26

def boot {

27

// where to search snippet

28

LiftRules.addToPackages("code")

29

30

// Build SiteMap

31

def sitemap(): SiteMap = SiteMap(

32

Menu.i("Home") / "index", // the simple way to declare a menu

33

34

Menu.i("Sometimes") / "sometimes" >> If(displaySometimes_? _,

35

S ? "Can't view now"),

36

37

// A menu with submenus

38

Menu.i("Info") / "info" submenus(

39

Menu.i("About") / "about" >> Hidden >> LocGroup("bottom"),

40

Menu.i("Contact") / "contact",

41

Menu.i("Feedback") / "feedback" >> LocGroup("bottom")

42

),

43

44

45

Menu.i("Sitemap") / "sitemap" >> Hidden >> LocGroup("bottom"),

46

47

Menu.i("Dynamic") / "dynamic", // a page with dynamic content

48

49

Param.menu,

50

51

Menu.param[Which]("Recurse", "Recurse",

52

{case "one" => Full(First())

53

case "two" => Full(Second())

54

case "both" => Full(Both())

55

case _ => Empty},

56

w => w.toString) / "recurse",

57

58

// more complex because this menu allows anything in the

59

// /static path to be visible