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.

7.3

S/SHtml

7.4

Boot

7.5

SiteMap

7.6

GUIDs

A core concept in Lift is GUIDs. GUIDs are globally unique identifiers used to associate some-

thing in the browser with a function on the server. GUIDs make Lift more secure because they

make replay attacks very difficult and GUIDs make it easier to develop complex, stateful, interac-

tive applications because the developer spends more time on business logic and less time on the

plumbing of it.

7.6.1

How GUIDs are generated

7.6.2

Where they are used

7.7

LiftRules

7.8

SessionVars and RequestVars

7.9

Helpers

7.10. CSS SELECTOR TRANSFORMS

85

7.10

CSS Selector Transforms

Lift 2.2-M1 introduced a new mechanism for transforming XHTML: CSS Selector Transforms

(CssBindFunc). The new mechanism provides a subset of CSS selectors that can be used to

transform NodeSeq => NodeSeq. Examples of this feature include:

• "#name" #> userName // replace the element with the id name with the variable user-

Name

• "#chat_lines *" #> listOfChats // replace the content of chat_lines with each ele-

ment of listOfChats

• ".pretty *" #> <b>Unicorn</b> // each element with CSS class pretty, replace con-

tent with <b>Unicorn</b>

• "dog=cat [href]" #> "http://dogscape.com" // set the href attribute of all ele-

ments with the dog attribute set to cat

• "#name" #> userName & "#age" #> userAge // set name to userName and age to

userAge

• "li *" #> userName & "li [class]" #> "foo" // set the contents of all <li> element with username and class to foo

• "li *" #> userName & "li [class+]" #> "foo" // set the contents of all <li> element with username and append foo to the class

• "*" #> <span>{userName}</span>

//

set

all

the

elements

to

<span>{userName}</span>

CSS Selector Transforms extends NodeSeq => NodeSeq... they are quite literally functions and

can be passes as a parameter to anything expecting NodeSeq => NodeSeq or returned as a result

for any method that returns NodeSeq => NodeSeq.

Let’s look at each of the pieces to see how they work.

First, you must import net.liftweb.util._ and import Helpers._ These packages in-

clude the classes and the implicit conversions that make the CSS Selector Tranforms work.

The transform is defined by: String representing selector #> transform value.

The selector is a String constant which implements the following subset of CSS Selectors:

• #id - selects the element with the specified id

• .class - selects all elements have a class attribute where one of the space-separated values

equals class

• attr_name=attr_value - selects all elements where the given attribute equals the given

value

• element_name - selects all the elements matching the name

• * - selects all elements

86

CHAPTER 7. CORE CONCEPTS

• @name - selects all elements with the specified name

• :button - selects all the elements with type="button"

• :checkbox - selects all the elements with type="checkbox"

• :file - selects all the elements with type="file"

• :password - selects all the elements with type="password"

• :radio - selects all the elements with type="radio"

• :reset - selects all the elements with type="reset"

• :submit - selects all the elements with type="submit"

• :text - selects all the elements with type="text"

You can put replacement rules after the selector:

• none (e.g., "#id") replaces all matching elements with the values

"#name" #> "David" // <span><span id="name"/></span> ->

<span>David</span>

• * (e.g., "#id *") replaces the content children of the matching elements with the values

"#name *" #> "David" // <span><span id="name"/></span> ->

<span><span id="name>David</span></span>

• *+ or *< (e.g., "#id *+") appends the value to the the content children nodes

"#name *+" #> "David" // <span><span id="name">Name:

</s-

pan></span> -> <span><span id="name>Name:

David</span></span>

• -* or >* (e.g., "#id -*") prepends the value to the the content children nodes

"#name -*" #> "David" // <span><span id="name"> Pol-

lak</span></span> -> <span><span id="name>David Pol-

lak</span></span>

• [attr] (e.g., "#id [href]") replaces the matching attribute’s value with the values.

"#link [href]" #> "http://dogscape.com"

// <a href="#" id="link">Dogscape</a> -> <a rel="nofollow" target="_blank" href="http://dogscape.com"

id="link">Dogscape</a>

• [attr+] (e.g., "#id [class+]") appends the value to the existing attribute.

"span [class+]" #> "error"

// <span class"foo">Dogscape</span> -> <span class"foo er-

ror">Dogscape</span>

• [attr!] (e.g., "#id [class!]") removes the matching value to the existing from.

"span [class!]" #> "error"

// <span class"error foo">Dogscape</span> -> <span

class"foo">Dogscape</span>

7.10. CSS SELECTOR TRANSFORMS

87

• ^^ - lifts the selected element to the root of the elements that are returned making it possible

to choose an element from a template

• ^* - lifts the selected element’s children to the root of the elements that are returned making

it possible to choose an element’s children from a template

The right hand side of the CSS Selector Transform can be one of the following:

• String – a String constant, for example:

"#name *" #> "David" // <span id="name"/> -> <span id="name">David</span>

"#name *" #> getUserNameAsString

• NodeSeq - a NodeSeq constant, for example:

"#name

*"

#>

<i>David</i>

//

<span

id="name"/>

->

<span

id="name"><i>David</i></span>

"#name *" #> getUserNameAsHtml

• NodeSeq => NodeSeq – a function that transforms the node (yes, it can be a CssBind-

Func):

"#name" #> ((n:

NodeSeq) => n % ("class" -> "dog"))

//

<span

id="name"/> -> <span id="name" class="dog"/>

• Bindable – something that implements the Bindable trait (e.g., MappedField and

Record.Field)

• StringPromotable – A constant that can be promoted to a String (Int, Symbol, Long

or Boolean). There is an automatic (implicit) conversion from Int, Symbol, Long or Boolean

to StringPromotable.

"#id_like_cats" #> true & "#number_of_cats" #> 2

• IterableConst – A Box, Seq, or Option of NodeSeq => NodeSeq, String, NodeSeq,

or Bindable. Implicit conversions automatically promote the likes of Box[String],

List[String], List[NodeSeq], etc. to IterableConst.

"#id" #> (Empty:

Box[String]) // <span><span id="id">Hi</span></span>

-> <span/>

"#id" #> List("a", "b", "c") // <span><span id="id"/></span> ->

<span>abc</span>

"#id [href]" #> (None:

Option[String]) <a id="id" href="dog"/> ->

<a id="id"/>

Note that if you bind to the children of a selected element, multiple copies of the element re-

sult from bind to an IterableConst (if the element has an id attribute, the id attribute will be

stripped after the first element):

1

"#line *" #> List("a", "b", "c") // <li id="line>sample</li> -> 2

// <li id="line">a</li><li>b</li><li>c</li>

3

4

"#age *" #> (None: Option[NodeSeq]) // <span><span id="age">Dunno</span></span> -> 5

// <span/>

88

CHAPTER 7. CORE CONCEPTS

The above use cases may seem a little strange (they are not quite orthogonal), but they address

common use cases in Lift. * IterableFunc – A Box, Seq, or Option of functions that transform Node-

Seq => String, NodeSeq, Seq[String], Seq[NodeSeq], Box[String], Box[NodeSeq], Option[String] or

Option[NodeSeq]. The same rules for handling multiple values in IterableConst apply to Iterable-

Func. Implicit conversions automatically promote the functions with the appropriate signature to

an IterableFunc.

You can chain CSS Selector Transforms with the & method:

"#id" #> "33" & "#name" #> "David" & "#chat_line" #> List("a", "b",

"c") & ClearClearable

CSS Selector Transforms offer an alternative to Lift’s traditional binding (See Helpers.bind()).

7.11. CLIENT-SIDE BEHAVIOR INVOKING SERVER-SIDE FUNCTIONS

89

7.11

Client-side behavior invoking server-side functions

7.12

Ajax

7.13

Comet

7.14

LiftActor

7.15

Pattern Matching

7.16

Type safety

7.17

Page rewriting

7.18

Security

90

CHAPTER 7. CORE CONCEPTS

Chapter 8

Common Patterns

91

92

CHAPTER 8. COMMON PATTERNS

8.1

Localization

Lift has broad support for localization at the page and element level.

8.1.1

Localizing Templates

The

locale

for

the

current

request

is

calculated

based

on

the

function

in

LiftRules.localeCalculator. By default, the function looks at the Locale in the HTTP

request. But you can change this function to look at the Locale for the current user by changing LiftRules.localeCalculator.

When a template is requested, Lift’s TemplateFinder looks for a template with the suf-

fix _langCOUNTRY.html, then _lang.html, then .html. So, if you’re loading /frog and

your Locale is enUS, then Lift will look for /frog_enUS.html, then /frog_en.html, then

/frog.html. But if your Locale is Czech, then Lift would look for /frog_csCZ.html, /frog_-

cs.html, and /frog.html. The same lookup mechanism is used for templates accessed via the

Surround (See Section 9.14) and Embed (See Section 9.13) snippets. So, at the template level, Lift offers very flexible templating.

Note: Lift parses all templates in UTF-8. Please make sure your text editor is set to UTF-8 encod-

ing.

8.1.2

Resource Lookup

Lift uses the following mechanism to look up resources. Localized resources are stored in tem-

plate files along-side your HTML pages. The same parser is used to load resources and the pages

themselves. A global set of resources is searched for in the following files: /_resources.html,

/templates-hidden/_resources.html, and /resources-hidden/_resources.html.

Keep in mind that Lift will look for the _resources file using the suffixes based on the Locale.

The resource file should be in the following format:

1

<resources>

2

<res name="welcome">Benvenuto</res>

3

<res name="thank.you">Grazie</res>

4

<res name="locale">Località</res>

5

<res name="change">Cambia</res>

6

</resources>

In addition to global resource files, there are per-page resource files (based on the current

Req.) If you are currently requesting page /foo/bar, the following resource files will also

be consulted: /foo/_resources_bar.html, /templates-hidden/foo/_resources_-

bar.html, and /foo/resources-hidden/_resources_bar.html (and all Locale-specific

suffixes.) You can choose to create a separate resource file for each locale, or lump multiple locales

into the _resources_bar.html file itself using the following format:

1

<resources>

2

<res name="hello" lang="en" default="true">Hello</res>

3

<res name="hello" lang="en" country="US">Howdy, dude!</res>

8.1. LOCALIZATION

93

4

<res name="hello" lang="it">Benvenuto</res>

5

<res name="thank.you" lang="en" default="true">Thank You</res>

6

<res name="thank.you" lang="it">Grazie</res>

7

<res name="locale" lang="en" default="true">Locale</res>

8

<res name="locale" lang="it">Località</res>

9

<res name="change" lang="en" default="true">Change</res>

10

<res name="change" lang="it">Cambia</res>

11

</resources>

8.1.3

Accessing Resources

Lift makes it easy to access resources.

From snippets: <span class="lift:Loc.hello">This Hello will be replaced if

possible</span> Note that the value after the . in the snippet invocation is used to look up the

resource name.

From code:

• S.loc("hello") - return a Box[NodeSeq] containing the localized value for the resource

named “hello”.

• S.??("Hello World") - look for a resource named “Hello World” and return the String

value for that resource. If the resource is not found, return “Hello World”.

8.1.4

Conclusion

Lift offers a broad range of mechanisms for localizing your application on a page-by-page and

resource-by-resource by-resource basis.

94

CHAPTER 8. COMMON PATTERNS

8.2

Dependency Injection

Dependency injection is an important topic in the Java world. It’s important because Java lacks

certain basic features (e.g., functions) that tend to bind abstract interfaces to concrete implementa-

tions. Basically, it’s so much easier to do MyInterface thing = new MyInterfaceImpl(),

so most developers do just that.

Scala’s cake pattern goes a long way to help developers compose complex behaviors by combining Scala traits. Jonas Bonér wrote an excellent piece on Dependency Injection.

The cake pattern only goes half way to giving a Java developer complete dependency injection

functionality. The cake pattern allows you to compose the complex classes out of Scala traits, but

the cake pattern is less helpful in terms of allowing you to make dynamic choices about which

combination of cake to vend in a given situation. Lift provides extra features that complete the

dependency injection puzzle.

8.2.1

Lift Libraries and Injector

Lift is both a web framework and a set of Scala libraries. Lift’s common, actor, json, and util

packages provide common libraries for Scala developers to build their application. Lift’s libraries

are well tested, widely used, well supported, and released on a well defined schedule (montly

milestones, quarterly releases).

Lift’s Injector trait forms the basis of dependency injection:

1

/**

2

* A trait that does basic dependency injection.

3

*/

4

trait Injector {

5

implicit def inject[T](implicit man: Manifest[T]): Box[T]

6

}

You can use this trait as follows:

1

object MyInjector extends Injector {...}

2

3

val myThing: Box[Thing] = MyInjector.inject

The reason that the instance of MyThing is in a Box is because we’re not guaranteed that MyIn-

jector knows how to create an instance of Thing. Lift provides an implementation of Injector

called SimpleInjector that allows you to register (and re-register) functions for injection:

1

object MyInjector extends SimpleInjector

2

3

def buildOne(): Thing = if (testMode) new Thing with TestThingy {} else new Thing with RuntimeThingy {}

4

5

MyInjector.registerInjection(buildOne _) // register the function that builds Thing

6

7

val myThing: Box[Thing] = MyInjector.inject

8.2. DEPENDENCY INJECTION

95

This isn’t bad... it allows us to define a function that makes the injection-time decision, and we can

change the function out during runtime (or test-time.) However, there are two problems: getting

Boxes for each injection is less than optimal. Further, globally scoped functions mean you have to

put a whole bunch of logic (test vs. production vs. xxx) into the function. SimpleInjector has

lots of ways to help out.

1

object MyInjector extends SimpleInjector {

2

val thing = new Inject(buildOne _) {} // define a thing, has to be a val so it's eagerly evaluated and registered

3

}

4

5

def buildOne(): Thing = if (testMode) new Thing with TestThingy {} else new Thing with RuntimeThingy {}

6

7

val myThingBox: Box[Thing] = MyInjector.injectval

8

9

myThing = MyInjector.thing.vend // vend an instance of Thing

Inject has a futher trick up its sleave... with Inject, you can scope the function... this is helpful

for testing and if you need to change behavior for a particular call scope:

1

MyInjector.thing.doWith(new Thing with SpecialThing {}) {

2

val t = MyInjector.thing.vend // an instance of SpecialThing

3

val bt: Box[Thing] = MyInjector.inject // Full(SpecialThing)

4

}

5

6

MyInjector.thing.default.set(() => new Thing with YetAnotherThing {}) // set the global scope

Within the scope of the doWith call, MyInjector.thing will vend instances of SpecialThing.

This is useful for testing as well as changing behavior within the scope of the call or globally. This

gives us much of the functionality we get with dependency injection packages for Java. But within

Lift WebKit, it gets better.

8.2.2

Lift WebKit and enhanced injection scoping

Lift’s WebKit offers broad ranging tools for handling HTTP requests as well as HTML manipula-

tion.

Lift WebKit’s Factory extends SimpleInjector, but adds the ability to scope the function

based on current HTTP request or the current container session:

1

object MyInjector extends Factory {

2

val thing = new FactoryMaker(buildOne _) {} // define a thing, has to be a val so it's eagerly

3

// evaluated and registered

4

}

5

6

MyInjector.thing.session.set(new Thing with ThingForSession {}) // set the instance that will be vended

7

// for the duration of the session

8

9

MyInjector.thing.request.set(new Thing with ThingForRequest {}) // set the instance that will be vended

10

// for the duration of the request

96

CHAPTER 8. COMMON PATTERNS

WebKit’s LiftRules is a Factory and many of the properties that LiftRules contains are

FactoryMakers. This means that you can change behavior during call scope (useful for testing):

1

LiftRules.convertToEntity.doWith(true) { ... test that we convert certain characters to entities}

Or based on the current request (for example you can change the rules for calculating the docType

during the current request):

1

if (isMobileReqest) LiftRules.docType.request.set((r: Req) => Full(DocType.xhtmlMobile))

Or based on the current session (for example, changing maxConcurrentRequests based on some

rules when a session is created):

1

if (browserIsSomethingElse) LiftRules.maxConcurrentRequests.session.set((r: Req) => 32)

2

// for this session, we allow 32 concurrent requests

8.2.3

Conclusion

Lift’s SimpleInjector/Factory facilities provide a powerful and flexible mechanism for

vending instances based on a global function, call stack scoping, request and session scoping and

provides more flexible features than most Java-based dependency injection frameworks without

resorting to XML for configuration or byte-code rewriting magic.

8.3

Modules

Lift has supported modules from the first version of the project in 2007. Lift’s entire handling of

the HTTP request/response cycle is open to hooks. Further, Lift’s templating mechanism where

resulting HTML pages are composed by transforming page content via snippets (See Section 7.1)

which are simply functions that take HTML and return HTML: NodeSeq => NodeSeq. Be-

cause Lift’s snippet resolution mechanism is open and any code referenced in Boot (See Section

7.4), any code can be a Lift “module” by virtue of registering its snippets and other resources in LiftRules. Many Lift modules already exist including PayPal, OAuth, OpenID, LDAP, and even

a module containing many jQuery widgets.

The most difficult issue relating to integration of external modules into Lift is how to properly

insert the module’s menu items into a SiteMap (See Section 3.2) menu hierarchy. Lift 2.2 introduces a more flexible mechanism for mutating the SiteMap: SiteMap mutators. SiteMap mutators

are functions that rewrite the SiteMap based on rules for where to insert the module’s menus in

the menu hierarchy. Each module may publish markers. For example, here are the markers for

ProtoUser:

1

/**

2

* Insert this LocParam into your menu if you want the

3

* User's menu items to be inserted at the same level

4

* and after the item

5

*/

6

final case object AddUserMenusAfter extends Loc.LocParam[Any]

8.3. MODULES

97

7

/**

8

* replace the menu that has this L