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.

import Helpers._

10

11

// our snippet

12

object TimeNow {

13

// create a function (NodeSeq => NodeSeq)

14

// that puts the current time into the

15

// body of the incoming Elem

16

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

17

}

This snippet must be in the snippet package so Lift knows how to find it by convention.

It is an object which is a singleton because the snippet has no state.

Lift calls the render method on a snippet unless you specify another method when you invoke

your snippet.

The snippet generates a function, NodeSeq => NodeSeq, that uses Lift’s CSS Selector Trans-

forms (See 7.10 on page 85) to insert the current time into the body of all HTML Elements: def

render = "* *" #> now.toString

13.5

Getting Ajaxy

The ClickMe snippet is a little more complex, but it demonstrates, especially on the “Second

Page” the power of Lift’s View First in which no particular component on the page is the dominant

component. Here’s the ClickMe code:

Listing 13.3: ClickMe.scala

1

// make sure this is the snippet package so Lift

2

// can find the snippet

3

package code

114

CHAPTER 13. FROM MVC

4

package snippet

5

6

// some inputs

7

import net.liftweb._

8

import util._

9

import Helpers._

10

import http._

11

import js.JsCmds._

12

13

// our snippet

14

object ClickMe {

15

// variables associated with the request

16

private object pos extends RequestVar(0)

17

private object cnt extends RequestVar(0)

18

19

// create a function (NodeSeq => NodeSeq)

20

// set the onClick method of the button

21

def render = {

22

// capture our position on the page

23

val posOnPage = pos.set(pos.is + 1)

24

25

"button [onclick]" #>

26

SHtml.ajaxInvoke(() => {

27

cnt.set(cnt.is + 1) // increment the click count

28

Alert("Thanks pos: "+posOnPage+

29

" click count "+cnt)

30

})

31

}

32

}

We define two RequestVars that hold request-scoped values. For Lift, the scope of a request is

the initial full HTML page load plus any Ajax requests associated with that page.

When the snippet’s render method is called, we capture the current value for the pos Request-

Var.

The snippet associates the invocation of an Ajax call with the button’s onclick method. When

the button is clicked, the function is invoked.

The function closed over the scope of the position of the button on the page. The buttons all share

the cnt RequestVar and thus for a single page load, the number of button-presses are counted.

If you have 5 different browser tabs open to the same page, each tab will have a unique page

count.

This demonstrates the component nature of Lift and why having complex items on a page means

not having a front-controller, but having lots of behaviors associated with lots of HTML elements.

13.6

Next Steps

If you want to see more of Lift’s snazzy Ajax and Comet, check out 2 on page 5. If you want to see more of the basics of SiteMap and snippets, check out 3 on page 11. If you want to see how Lift

does forms, check out 4 on page 27.

Part II

Recipes

115

Chapter 14

Dynamic html tables created from

DB.runQuery()

14.1

Problem

What I’m trying is:

1. query the SQL server via DB.runQuery()

2. put the result (multiple, rows and columns) into a Table structure like this:

1

<table>

2

<thead>

3

<tr><th></th></tr>

4

</thead>

5

<tbody>

6

<tr><td></td></tr>

7

</tbody>

8

</table>

14.2

Solution

The DB.runQuery(sql_query_string) method returns (List[String], List[List[String]]), to put that in

a table, your view looks like:

1

<table class="lift:MySnippet">

2

<thead>

3

<tr><th id="my_th">Field Name</td></tr>

4

</thead>

5

<tbody>

6

<tr id="my_tr"><td>An item</td></tr>

7

</tbody>

8

</table>

And your snippet uses CSS Selector Transforms (See Section 7.10) and looks like:

117

118

CHAPTER 14. DYNAMIC HTML TABLES CREATED FROM DB.RUNQUERY()

1

object MySnippet {

2

def render = {

3

val (fieldNames: List[String], fieldValues: List[List[String]]) = DB.runQuery(...)

4

5

"#my_th *" #> fieldNames &

6

"#my_tr *" #> fieldValues.map(values => "td *" #> values)

7

}

8

}

Chapter 15

Dynamically choosing content

15.1

Problem

I want to to keep design completely separated from logic and I am bit stuck. I have a page that

loads different pieces of html depending on some variables and it also has some ajax code so it may

load new pieces of html. So far, that page uses only one snippet that has the logic to decide what

html should be loaded. So here is the question, how should the snippet get an only-with-design

piece of html to bind data to it.

15.2

Solution

Snippets are evaluated recursively... this means that you can return markup from a snippet that

contains other snippets.

The other thing to pay attention to is the <lift:embed> snippet (See Section 9.13).

Combining the two:

Main page:

1

<html><body> Stuff here

2

<div class="lift:ChooseBehavior">Different behavior will go here</div>

3

</body></html>

The snippet:

1

object ChooseBehavior {

2

def render = someState match {

3

case ShowData => <lift:embed what="_showData" />

4

case EditData => <lift:embed what="_editData" />

5

case AjaxThing => <lift:embed what="_ajaxThing" />

6

}

7

}

Then your designer need only edit the main page and each of the templates, and then you wire

them together.

119

120

CHAPTER 15. DYNAMICALLY CHOOSING CONTENT

Chapter 16

Ajax Forms

121

122

CHAPTER 16. AJAX FORMS

Chapter 17

Protecting REST APIs

17.1

Problem

I want to expose part of my site as authenticated REST, but with custom authentication (not the

HTTP based authentication).

Right now, I’m thinking of using a custom dispatch, but that means I’ll have to check every request

in the request handler itself to see if it is authenticated, right?

Authentication is just a SessionVar on the server, so it also implies I need a way to pass the session

identifier back and forth between the REST client and the service. If it were a cookie I think it

would be transparent, but I think Lift adds te session ids to the URLs (at least that’s what I see in

my address bar).

So, assuming I have a public "login" REST call that sets a SessionVar, how do I pass this transar-

ently to the REST client? I have thought about a token system as well, but that seems like copying

the session system.

Any suggestions?

17.2

Solution

If you’ve got a:

1

object MyService extends RestHelper {

2

....

3

}

And:

1

val ensureSession: PartialFunction[Req, Unit] = {

2

case _ if isLoggedIn =>

3

}

then in Boot:

123

124

CHAPTER 17. PROTECTING REST APIS

1

import net.liftweb.util.Helpers._

2

3

LiftRules.dispatch.append(ensureSession guard MyService)

This is a simple way to compose PartialFunctions and put a guard around all the elements

of a PartialFunction.

Chapter 18

URI-based locale selection

18.1

Problem

I’m evaluating Lift and one thing I miss, or cannot see how toimplement, is the ability to have the

locale determined from an URI-pattern. In Struts2 I have:

1

namespace="/{request_locale}"

So I can have an action (restful) invoked on an URI=/no/companies/company/1 and it will call

my CompanyAction with id=1 and the locale

set to no If called from URI=/en/companies/company/1 it will callthe same CompanyAction

but the locale will be set to "en".

So my question is: Is it possible to teach Lift to retrieve the locale based on some uri-pattern, so

that it will try to resolve my *.xhtml after the /{request_locale} part?

/no/index.xhtml

/en/index.xhtml

Should then map to the same templates but with different locale.

18.2

Solution

This is an ideal use of URL rewriting.

You have to hook up the module in Boot.scala with: UrlLocalizer.init().

You can see a complete runnable example at DPP’s GitHub Starting Point.

Here’s the code:

125

126

CHAPTER 18. URI-BASED LOCALE SELECTION

1

package code

2

package lib

3

4

import net.liftweb._

5

import http._

6

import provider._

7

import common._

8

9

import java.util.Locale

10

11

object UrlLocalizer {

12

// capture the old localization function

13

val oldLocalizeFunc = LiftRules.localeCalculator

14

15

/**

16

* What are the available locales?

17

*/

18

val locales: Map[String, Locale] =

19

Map(Locale.getAvailableLocales.map(l => l.toString -> l) :_*)

20

21

object currentLocale extends RequestVar(Locale.getDefault)

22

23

/**

24

* Extract the locale

25

*/

26

def unapply(in: String): Option[Locale] =

27

if (currentLocale.set_?) None // don't duplicate

28

else locales.get(in) // if it's a valid locale, it matches

29

30

/**

31

* Calculate the Locale

32

*/

33

def calcLocale(in: Box[HTTPRequest]): Locale =

34

if (currentLocale.set_?) currentLocale.get

35

else oldLocalizeFunc(in)

36

37

/**

38

* Initialize the locale

39

*/

40

def init() {

41

// hook into Lift

42

LiftRules.localeCalculator = calcLocale

43

44

// rewrite requests with a locale at the head

45

// of the path

46

LiftRules.statelessRewrite.append {

47

case RewriteRequest(ParsePath(UrlLocalizer(locale) :: rest,

48

_, _, _), _, _) => {

49

currentLocale.set(locale)

50

RewriteResponse(rest)

51

}

52

}

53

}

54

}

Chapter 19

Embedding JavaScript in an HTML page

19.1

Problem

What am I doing wrong? I’m trying to output a javascript object into the page (so my front end

guy can do some stuff with the data without parsing it out of elements by id) but it’s replacing all

the double quotes with &quot; (only in view source - if I inspect it then firebug converts them to

double quotes again)

I’ve copied the example from EXPLORING LIFT, but it still does the same:

1

& ".data_as_object *" #> {

2

JsCrVar("myObject", JsObj(("persons", JsArray(

3

JsObj(("name", "Thor"), ("race", "Asgard")),

4

JsObj(("name", "Todd"), ("race", "Wraith")),

5

JsObj(("name", "Rodney"), ("race", "Human"))

6

))))

Becomes:

1

<div class="data_as_object" style="display: none;">var myObject =

2

{&quot;persons&quot;: [{&quot;name&quot;: &quot;Thor&quot;,

3

&quot;race&quot;: &quot;Asgard&quot;}, {&quot;name&quot;:

4

&quot;Todd&quot;, &quot;race&quot;: &quot;Wraith&quot;},

5

{&quot;name&quot;: &quot;Rodney&quot;, &quot;race&quot;:

6

&quot;Human&quot;}]

7

};</div>

I’ve noticed that if what I’m outputting is a number rather than a string then it’s fine.

19.2

Solution

Try:

1

& ".data_as_object *" #> {

2

Script(JsCrVar("myObject", JsObj(("persons", JsArray(

127

128

CHAPTER 19. EMBEDDING JAVASCRIPT IN AN HTML PAGE

3

4

JsObj(("name", "Thor"), ("race", "Asgard")),

5

JsObj(("name", "Todd"), ("race", "Wraith")),

6

JsObj(("name", "Rodney"), ("race", "Human"))

7

)))))

JsExp are also Nodes, so they render out, but they render out escaped. Putting Script() around

them turns them into:

1

<script>

2

// <![CDATA[

3

....

4

]]>

5

</script>

Part III

Questions and Answers

129

Chapter 20

Scaling

Lift is a web framework built on the Scala programming language. Lift takes advantage of many

of Scala’s features that allow developers to very concisely code secure, scalable, highly interactive

web applications. Lift provides a full set of layered abstractions on top of HTTP and HTML from

"close to the metal" REST abstractions up to transportation agnostic server push (Comet) support.

Scala compiles to JVM byte-code and is compatible with Java libraries and the Java object model.

Lift applications are typically deployed as WAR files in J/EE web containers... Lift apps run in

Tomcat, Jetty, Glassfish, etc. just like any other J/EE web application. Lift apps can generally

be monitored and managed just like any Java web app. Web Applications, Sessions, and State.

All web applications are stateful in one way or another. Even a "static" web site is made up of

the files that are served... the application’s state is defined in those files. The site content may

be served out of a database, but the content served does not depend on identity of the user or

anything about the HTTP request except the contents of the HTTP request. These contents can

include the URI, parameters, and headers. The complete value of the response can be calculated

from the request without referencing any resources except the content resources. For the purpose

of this discussion, I will refer to these as session-less requests. News sites like the UK Guardian,

MSNBC, and others are prototypical examples of this kind of site. Sessions. Some applications

are customized on a user-by-user basis. These applications include the likes of Foursquare and

others where many HTTP requests make up a "session" in which the results of previous HTTP

requests change the behavior of future HTTP requests. Put in concrete terms, a user can log into

a site and for some duration, the responses are specific to that user. There are many mechanisms

for managing sessions, but the most common and secure method is creating a cryptographically

unique token (a session id), and putting that token in the Set-Cookie response header such that

the browser will present that Cookie in subsequent HTTP requests for a certain period of time.

The server-side state is referenced by the Cookie and the state is made available to the web appli-

cation during the scope of servicing the request and any mutations the web app makes to session

state during the request are kept on the server and are available to the application in subsequent

requests. Another available technique for managing state is to serialize application state in the

Cookie and deliver it to the browser such that the server is not responsible for managing state

across requests. As we’ve recently discovered, this is a tremendously insecure way to manage

application state. Further, for any moderately complex application, the amount of data the needs

to be transferred as part of each request and response is huge. Migratory Sessions. Many web

application managers allow for server-managed sessions to migrate across a cluster of web appli-

cation servers. In some environments such as Ruby on Rails, this is a hard requirement because

131

132

CHAPTER 20. SCALING

only one request at a time can be served per process, thus for any moderate traffic site, there

must be multiple processes serving pages. There are many strategies for migrating state across

processes: storing state on disk, in memcached, in a database (relational or NoSQL), or having

some proprietary cluster communications protocol. In any of these scenarios sessions can migrate

across the grid of processes serving requests for a given web application. Web applications that

support migratory state are often referred to as "stateless" because the session state does not re-

side in the same process as the web application. Session Affinity. Some applications require that

all requests related to a particular session are routed to the same process and that process keeps

session-related content in local memory. In a cluster, there are multiple mechanisms for achiev-

ing session affinity... the two most popular being HAProxy and Nginx. Availability, Scalability,

Security, Performance, and User Experience. There are many vectors on which to measure the

overall-quality of a web application. Let’s take a quick peek at each one. Availability. Avail-

ability of an application is the amount of time it gives a meaningful response to a request. Highly

available applications generally span multiple pieces of hardware and often multiple data centers.

Highly available applications are also typically available during upgrades of part of the system

that makes up the application. Highly available applications have very few single points of failure

and those single points of failure are usually deployed on very reliable hardware. Scalability. A

scalable application can, within certain bounds, respond with similar performance to increased

load by adding hardware to process more load. No system is infinitely or linearly scalable. How-

ever, many systems have grossly disproportionate load demands such that, for example, you can

add a lot of web application front-ends to a Rails application before there’s enough load on the

back-end RDBMS such that scaling is impaired.

Security. The Internet is a dangerous place and no request that is received from the Internet can

be trusted. Applications, frameworks, systems and everything else must be designed to be se-

cure and resist attacks. The most common attacks on web application are listed in the OWASP

Top Ten. Performance. Web application performance can be measured on two vectors: response

time to a request and system resources required to service the request. These two vectors are

inter-dependent. User Experience. The user experience of a web app is an important measure of

its quality. User experience can be measured on many different vectors including perceived re-

sponsiveness, visual design, interactivity, lack of "hicups", etc. Ultimately, because we’re building

applications for users, the user experience is very important. Lift’s trade-offs. Given the number

and complexity related to the quality of a web application, there are a lot of trade-offs, implicit

and explicit, to building a framework that allows developers and business people to deliver a

great user experience. Let’s talk for a minute about what Lift is and what it isn’t. Lift is a web

framework. It provides a set of abstractions over HTTP and HTML such that developers can write

excellent web applications. Lift is persistence agnostic. You can use Lift with relational databases,

file systems, NoSQL data stores, mule carts, etc. As long as you can materialize an object into

the JVM where Lift is running, Lift can make use of that object. Lift sits on top of the JVM. Lift

applications execute in the Java Virtual Machine. The JVM is a very high performance computing

system. There are raging debates as to the relative performance of JVM code and native machine

code. No matter which benchmarks you look at, the JVM is a very fast performer. Lift apps take

advantage of the JVM’s performance characteristics. Moderately complex Lift apps that access the

database can serve 1,000+ requests per second on quad-core Intel hardware. Even very complex

Lift apps that make many back-end calls per request can serve hundreds of requests per second on