Up: Part III

C Lift Helpers

C.1 Introduction

Lift provides a fairly useful collection of helper artifacts. The helpers are essentially utility functions that minimize the need for boilerplate code. This appendix is intended to introduce some of the more common utility classes and objects to you so that you’re familiar with them. If you would like more details, you can look at the API documentation for the net.liftweb.util package.

C.2 Box (or Scala’s Option class on steroids)

net.liftweb.util.Box (or Scala’s scala.Option class on steroids) is a utility class that mimics Scala’s Option type (also heavily used inside Lift). To understand some of the underlying concepts and assumptions, let’s take a quick look at Option class first. The Option class allows a type-safe way of dealing with a situation where you may or may not have a result. Option has two values, either Some(value), where value is actually the value, and None, which is used to represent nothing. A typical example for Option is outlined using Scala’s Map type. Listing C.2↓ shows a definition of a Map, a successful attempt to get the value of key a, and an attempt to get the value of key i. Notice that when we retrieved the existing key-value pair for a, the value returned was Some(A) and when we asked for the value of key i, we received None.
Option and Map example
scala> val cap = Map("a" -> "A", "b" -> "B") 
cap: scala.collection.immutable.Map[java.lang.String,java.lang.String] = 
  Map(a -> A, b -> B)
​
scala> cap.get("a")  
res1: Option[java.lang.String] = Some(A)
​
scala> cap.get("i") 
res2: Option[java.lang.String] = None
Getting the value out of an Option is usually handled via Scala’s matching mechanism or via the getOrElse function, as shown in Listing C.2↓:
Fetch value from an Option
def prettyPrint(foo: Option[String]): String = foo match {
  case Some(x) => x
  case None => "Nothing found."
}
​
// Which would be used in conjunction with the previous code:
scala> prettyPrint(cap.get("a")) 
res7: String = A
​
scala> prettyPrint(cap.get("i")) 
res8: String = Nothing found.
Box in Lift covers the same base functionality as Option but expands the semantics for missing values. If we have an Option that is None at some point, we can’t really tell why that Option is None, although in many situations, knowing why would be quite helpful. With Box, on the other hand, you have either have a Full instance (corresponding to Some with Option) or an instance that subclasses EmptyBox (corresponding to None). EmptyBox can either be an Empty instance or a Failure instance incorporating the cause for the failure. So you can think of Box as a container with three states: full, empty, or empty for a particular reason. The Failure case class takes three arguments: a String message to describe the failure, a Box[Throwable] for an optional exception related to the failure, and a Box[Failure] for chaining based on earlier Failures.
As an example of how we can use Box instances in real code, consider the case where we have to do a bunch of null checks, perform an operation, and then perform more null checks, other operations, and so on. Listing C.2↓ shows an example of this sort of structure.
Pseudocode nested operations example
    x = getSomeValue();
    if (x != null) {
      y = getSomeOtherValue();
      if (y != null) {
        compute(x, y);
      }
    }
​
This is tedious and error-prone in practice. Now let’s see if we can do better by combining Lift’s Box with Scala’s for comprehensions as shown in Listing C.2↓.
Box nested operations example
​
    def getSomeValue(): Box[Int] = Full(12)     
    def getSomeOtherValue(): Box[Int] = Full(2)
    
    def compute(x: Int, y: Int) = x * y
  
    val res = for ( x <- getSomeValue();
                    y <- getSomeOtherValue() if x > 10) yield compute(x, y)
    println(res) 
In Listing C.2↑, we have two values, x and y, and we want to do some computation with these values. But we must ensure that computation is done on the correct data. For instance, the computation cannot be done if getSomeValue returns no value. In this context, the two functions return a Box[Int]. The interesting part is that if either or both of the two functions return an Empty Box instead of Full (Empty impersonating the nonexistence of the value), the res value will also be Empty. However, if both functions return a Full (like in Listing C.2↑), the computation is called. In our example the two functions return Full(12) and Full(2), so res will be a Full(24).
But we have something else interesting here: the if x > 10 statement (this is called a “guard” in Scala). If the call to getSomeValue returns a value less than or equal to 10, the y variable won’t be initialized, and the res value will be Empty. This is just a taste of some of the power of using Box for comprehensions; for more details on for comprehensions, see The Scala Language Specification, section 6.19, or one of the many Scala books available.
Lift’s Box extends Option with a few ideas, mainly the fact that you can add a message about why a Box is Empty. Empty corresponds to Option’s None and Full to Option’s Some. So you can pattern match against a Box as shown in Listing C.2↓.
Box example
a match {
  Full(author) => Text("I found the author " + author.niceName)
  Empty => Text("No author by that name.")
  // message may be something like "Database disconnected."
  Failure(message, _, _) => Text("Nothing found due to " + message) 
}
def confirmDelete {     
  (for (val id <- param("id");     // get the ID           
        val user <- User.find(id)) // find the user                                                                                
  yield {          
    user.delete_!                   
    notice("User deleted")          
    redirectTo("/simple/index.html")        
  }) getOrElse {error("User not found"); redirectTo("/simple/index.html")}    
}
​
In conjunction with Listing C.2↑, we can use other Box functions, such as the openOr function shown in Listing C.2↓.
openOr example
lazy val UserBio = UserBio.find(By(UserBio.id, id)) openOr (new UserBio)
def view (xhtml: NodeSeq): NodeSeq = passedAuthor.map({ author => 
  // do bind, etc here and return a NodeSeq
}) openOr Text("Invalid author") 
We won’t be detailing all of the Box functions here, but a few words on the most common function might be benficial.
Function name Description Short example. Assume myBox is a Box
openOr Returns the value contained by this Box. If the Box is Empty myBox openOr “The box is Empty”
map Apply a function on the values of this Box and return something else. myBox map (value => value + “ suffix”)
dmap Equivalent with map(..) openOr default_value. The default value will be returned in case the map is Empty myBox dmap(“default”)(value => value + “ suffix”)
!! If the argument is null in will return an Empty, otherwise a Full containing the arguent’s value. Note this this is a method on the Box object, not a given Box instance. Box !! (<a reference>)
?~ Transforms an Empty to a Failure and passing a message. If the Box is a Full it will just return this. myBox ?~ (“Error message”)
isDefined Returns true if this Box contains a value myBox isDefined
isEmpty Retun true is this Boxis empty myBox isEmpty
asA[B] Return a Full[B] if the content of this Box is of type B, otherwise return Empty myBox asA[Person]
isA[B] Return a Full[B] if the contents of this Box is an instance of the specified class, otherwise return Empty myBox isA[Person]
Note that Box contains a set of implicit conversion functions from/to Option and from/to Iterable.
Remember that Box is heavily used in Lift and most of the Lift’s API’s operates with Boxes. The rationale is to avoid null references and to operate safely in context where values may be missing. Of course, a Box can be set to null manually but we strongly recommend against doing so. There are cases, however, where you are using some third party Java libraries with APIs that return null values. To cope with such cases in Lift you can use the !! function to Box that value. Listing C.2↓ shows how we can deal with a possible null value.
Null example
​
var x = getSomeValueThatMayBeNull();
​
var boxified = Box !! x
​
In this case the boxified variable will be Empty if x is null or Full(x) if x is a valid value/reference..

C.3 ActorPing

It provides convenient functionality to schedule messages to Actors.
ActorPing example
// Assume myActor an existing Actor
// And a case object MyMessage
​
// Send the MyMessage message after 15 seconds
ActorPing.schedule(myActor, MyMessage, 15 seconds)
​
// Send the MyMessage message every 15 seconds. The cycle is stopped 
// if recipient actor exits or replied back with UnSchedule message
ActorPing.scheduleAtFixedRate(myActor, MyMessage, 0 seconds, 15 seconds)

C.4 ClassHelpers

Provides convenient functions for loading classes using Java reflection, instantiating dinamically loaded classes, invoking methods vis reflection etc.
ClassHelper example
import _root_.net.liftweb.util.Helpers._
​
// lookup the class Bar in the three packages specified in th list
findClass("Bar", "com.foo" :: "com.bar" :: "com.baz" :: Nil)
​
invokeMethod(myClass, myInstance, "doSomething")

C.5 CodeHelpers

Provides a convenient way of telling why a boolean expression failed. For instance we are seeing manytime code like:
Expression example
var isTooYoung = false;
var isTooBig = false;
var isTooLazy = true;
​
var exp = isTooYoung && isTooBig && isTooLazy
​
As you can see we have no way of telling if the exp was false because of isTooYoung, isTooBig or isTooLazy unless we test them again. But let’s see this:
CodeHelpers example
​
import net.liftweb.util._
import net.liftweb.util.MonadicConversions._
​
val exp = (isTooYoung ~ "too young") &&  
          (isTooBad ~ "too bad") &&  
          (isToLazy ~ "too lazy")
​
println(exp match {  
  case False(msgs) => 
    msgs mkString("Test failed because it is ’", "’ and ’", "’.")  
  case _ => "success"  
  }) 
Now if exp is a False we can tell why it failed as we have the messages now.

C.6 ControlHelpers

Provides convenient functions for try/catch situations. For example:
ControlHelpers example
​
tryo {
  // code here. Any exception thrown here will be silently caught
}
​
 
tryo((e: Throwable) => println(e)) {
  // code here. Any exception here willbe caught add passed to 
  // the above function.
}
​
tryo(List(classOf[ClassNotFoundException], classOf[IOException])) {
  // code here. If IOException or ClassNotFoundException is thrown 
  // (or a subclass of the two) they will be ignored. Any other
  // exception will be rethrown.
}

C.7 CSSHelpers

This provide a convenient functionality to fix relative root paths in CSS (Cascade Stylesheet) files. Here is an example:
CSSHelper example
Assume this entry in a CSS file:
​
.boxStyle {
  background-image: url(’/img/bkg.png’)
}
​
​
//in your code you can say
​
CSSHelpers.fixCSS(reader, "/myliftapp")
​
// where reader is a java.io.Reader that provides the 
// content of the CSS file.
Now if your application is not deployed in the ROOT context path (“/”) and say it is deployed with the context root /myliftapp then the background picture will probably notbe found. Say http://my.domain.com/img/bkg.png is an unknown path. However http://my.domain.com/myliftapp/img/bkg.png is known. In the example above we are calling fixCSS so that it will automatically replace the root relative paths such that background-image: url(’/img/bkg.png’) becomes background-image: url(’/myliftapp/img/bkg.png’). To use that in your lift application you can do:
fixCSS example
def boot(){
    ...
    LiftRules.fixCSS("styles" :: "theme" :: Nil, Empty)
    ...
}
When the /styles/theme.css file Lift will apply the prefix specified. But in this case we provided an Empty Box. This actually means that Lift will apply the context path returned by S.contextPath function which as you know returns the context path from the HttpSession.
Internally when you call fixCSS a dispatch function is automatically created and pre-pended to LiftRules.dispatch. This is needed in order to intercept the browser request to this .css resource. Also internally we are telling Lift the this resource must be server by Lift and not by container.
The way it works internally is that we are using Scala combinator parsers to augment only the root relative paths with the given prefix.

C.8 BindHelpers

Binders are extensiveley discussed in other chapters so we won’t reiterate them here.
Choose template XML
<lift:CountGame.run form="post">
  <choose:guess>
    Guess a number between 1 and 100.<br/>
    Last guess: <count:last/><br />
    Guess: <count:input/><br/>
    <input type="submit" value="Guess"/>
  </choose:guess>
  <choose:win>
    You Win!!<br />
    You guessed <count:number/> after <count:count/> guesses.<br/>
  </choose:win>
</lift:CountGame.run>
You can use the Helpers.chooseTemplate method to extract portions of a given XML input:
Choose template Scala code
import net.liftweb.util._
import Helpers._
​
class CountGame {
  def run(xhtml: NodeSeq): NodeSeq = {
    ... 
    chooseTemplate("choose", "win", xhtml);
  }
}
So in the snippet conditionally we can choose between parts of the snippet template. In the case above only the childs of <choose:win> node will be returned by the snippetfunction, hence rendered.

C.9 HttpHelpers

This provides helper functions for HTTP parameters manipulation, URL encoding/decoding etc. However there is some interesting functionality available that lets you choose between tags of a snippet.

C.10 JSON

Lift provides its own JSON parser if you ever need one. At a first glance it may be a bit redundant with Scala’s JSON parser but infact Scala’sparser has its own problems with large JSON objects hence List’s uses its own JSON parser implemented of course using combinator parsers.

C.11 LD

Provides utility functions for calculating the distance between words  [F]  [F] http://en.wikipedia.org/wiki/Levenshtein_distance

C.12 ListHelpers

Provides utility functions for manipulating lists that are not provided by Scala libraries.

C.13 NamedPartialFunctions

Provides extremly useful functions for invoking partial functions that are chained in lists of functions.
NamedPF example
var f1: PartialFunction[Int,Int] = {
  case 10 => 11
  case 12 => 14
}
​
var f2: PartialFunction[Int,Int] = {
  case 20 => 11
  case 22 => 14
}
​
​
NamedPF(10, f1 :: f2 :: Nil)
​
Remember that many LiftRules variable are RuleSeq-s. Meaning that most of the times we re talking about lists of partial functions. Hence internally lift uses NamedPF for invoking such functions that are ultimately provided by the user. Please see LiftRules.dispatch

C.14 SecurityHelpers

Provides various functions used for random number generation, encryption/decriptions (blowfish), hash calculations (MD5, SHA, SHA-256) and so on.

C.15 TimeHelpers

Utility functions for time operations. For instance if also provides a set of implicit conversion functions that allow you to type “10 seconds” and returns the value in milliseconds.
Up: Part III

(C) 2012 Lift 2.0 EditionWritten by Derek Chen-Becker, Marius Danciu and Tyler Weir