This blog post will most likely appeal to Scala headed and Lift handed programmers, so if you don’t fall into this category… You have been warned !
Scala is an acronym that stands for SCAlable LAnguage. It is a statically typed functional and object oriented programming language that runs on the Java Virtual machine (JVM) and the .NET common language runtime (CLR). It has this feel of a truly dynamically typed language but really it is not dynamically typed.
Scala on its functional side is a branch of the ML languages (example: Haskell), they are known to be strongly typed unlike their homoiconic and dynamically typed functional language counterpart like LISP or Dylan.
The main principle of Scala is that functional programming complements object oriented programming, despite the surface contradictions. Functional programming places her emphasis on immutable values and side effect free functions as opposed to object oriented programming which is all about state that is mutated in objects and methods that can modify state that are out of their scope. But combining these two paradigms in Scala has proven to be worthwhile. Scala makes it possible to create immutable objects and also apply OO goodness. In Scala, every value is an object and every function an object. (Wampler, D., 2008)
The core advantages of Scala are:
• Scala is functional which helps to write lesser and more concise code making it easier to test and maintain.
• Because Scala is functional in nature it embraces a lot of mathematical concepts, which emphasise correctness from a logical point of view. This is very important and infact can help reduce bugs in the software system.
• Scala introduces better ways of doing object oriented programming, in terms of composability (using mixins and traits) and scalable design.
• Scala provides a better model and much more principled approached for dealing with concurrency issues. Scala provides an Actors library that is similar to the one in Erlang. Actors are nothing more than concurrent process actions that communicate by exchanging messages. Actors can also view as active objects, based on this a method call corresponds to sending a message. The Scala Actors can also do asynchronous and synchronous messaging.
Lift is a non-mvc web framework written in Scala. Lift‘s approach to web development, is coined “view first“. With this approach a component of web page is an instance of a Scala object, these are called snippets in Lift.
Snippets basically are bindings between the view and a function that could contain some business logic and primarily transforms input XML to output XML or put in another way renders dynamic or interactive feedback to the View Template. In the view any tag with the Lift namespace is a snippet(Pollak, 2010).
<lift:someclass.function/> #binding to a function or <lift:someclass/> #binding to a render function... if defined will be displayed on page load by default.
They could be easily mistaken to be controllers (like i did
), but they are not.
So far it appears that Lift is elegant and has a productive sensation. I appreciate the fact that it does a lot of the plumbing for me, especially with regards to form rendering and security etc….
This is a web framework that i feel possesses elements of rhythm, tempo, melody, harmony, timbre, articulation, and dynamics. It leverages the power of Scala‘s xml and actors libraries also.
And because Lift is based on Scala, it is possible to play with legacy java code seamlessly and combine both functional and OO features like pattern matching, closures and high order functions etc.
Lift is one of the reasons why Scala is here to stay !
Almost all web applications i have been involved with have had varying numbers of web forms. Usually the state of these web forms are required to be maintained across instances.
This provides valuable information on the status quo and offers varying forms of feedback to users, hence it be referred to as a user friendly application.
Alright, enough chit chat, let’s look at some code. I have previously used this same use case while introducing my self to other web frameworks, but found it to be more intriguing with Lift.
In the following example, I use a simple video rental form application to highlight some of Lift’s features. Hopefully you got maven and Git installed, You can find the full source for this example here on Github and also a working example here.
The Web.xml has got very little mark up in it that points to the LiftFilter class and defines the URL pattern.
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <filter> <filter-name>LiftFilter</filter-name> <display-name>Lift Filter</display-name> <description>The Filter that intercepts lift calls</description> <filter-class>net.liftweb.http.LiftFilter</filter-class> </filter> <!-- <init-param> <param-name>bootloader</param-name> <param-value>path to my custom Boot class</param-value> </init-param> --> <filter-mapping> <filter-name>LiftFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
All lift application must have a Boot class. This is basically an expressive configuration class where lift allows you to define Rules that modify lift’s environment. Lift also allows defining a custom Boot instance using the init param tag, this custom Boot class must extend Bootable class and provide implementation for the boot method. The boot method can run only once.
class Boot {
def boot {
// where to search snippet
LiftRules.addToPackages("com.liftforms")
// Build SiteMap
val entries = Menu(Loc("Home", List("index"), "Home", Hidden)) ::
Menu(Loc("View", List("view"), "View", Hidden)) :: Nil
// Setting the sitemap
LiftRules.setSiteMap(SiteMap(entries: _*))
}
}
The LiftRules.addToPackages methods tells lift where to find snippets. By default you will get this code for free with the maven Lift archetype. URLs in Lift are pretty, the Menu/setSiteMap methods allows your to define meaningful urls for web pages and more.
The snippet class below extends the StatefulSnippet trait. This trait is used to maintain state of the form variables. Lift does not require using HTML tags for form elements directly, instead Lift provides generator functions on net.liftweb.http.SHtml. This allows Lift to set up all of the internal plumbing which kept my code relatively simple to read and separate from the view.
Below is example code for the forms class “RentingForm.scala”. Notice that is extends StatefulSnippet to help it manage state and the “class” -> “required” is used to meet the criteria for the jquery client side validation library. For more on this click here.
class RentingForm extends StatefulSnippet { // RentingForm snippet extending StatefulSnippet
var dispatch: DispatchIt = {
case "rent" => rentForm _ // call rentForm function
}
var (name, address, email, movieType, dateOfHire, numberOfDays, discount) = ("", "", "", "", "", "", "") // Tuple of form variables
val movieSeq = Seq("" -> "Select Movie Type", "Sci-fi" -> "Sci-fi", "Horror" -> "Horror", "Comedy" -> "Comedy", "Suspense" -> "Suspense", "Romance" -> "Romance") // Sequence of movie types
val discountMap = Map("Regular Customer" -> "Regular Customer", "New Customer" -> "New Customer") // Map of customer types
// definition for rentForm
def rentForm(xhtml: NodeSeq): NodeSeq = {
dispatch = {
case name if name != "" => showDetails _ // call show details function if name is not equal to ""
}
val discounts = SHtml.radio(discountMap.keys.toList, Full("New Customer"), discount = _, "class" -> "required") // radio button
val submitLabel = "Confirm"
// Binding Form fields and components to template
bind("v", xhtml,
"name" -> SHtml.text(name, name = _, "class" -> "required"), // text field
"address" -> SHtml.textarea(address, address = _, "rows" -> "5", "class" -> "required"), // text area
"email" -> SHtml.text(email, email = _, "class" -> "required email"), // text field
"movie" -> SHtml.select(movieSeq, Empty, movieType = _, "class" -> "required"), // select options combo box
"date" -> SHtml.text(dateOfHire, dateOfHire = _, "class" -> "required date", "id" -> "datePicker"), // text field
"numberOfDays" -> SHtml.text(numberOfDays, numberOfDays = _, "class" -> "required"), // text field
"regular" -> discounts(0), // radio buttons 0
"new" -> discounts(1), // radio button 1
"rtxt1" -> (discountMap.head.key), //radio button label 0
"rtxt2" -> (discountMap.last.key), // radio button label 1
"submit" -> SHtml.submit(submitLabel, () => {}, "id" -> "submit")) // submit button
}
// definition for showDetails
def showDetails(xhtml: NodeSeq): NodeSeq = {
val submitLabel = "Edit"
// Binding Form fields and components to template
bind("v", xhtml,
"name" -> Text(name), // plain text rendering form variable to template
"address" -> Text(address), // plain text rendering form variable to template
"email" -> Text(email), // plain text rendering form variable to template
"movie" -> Text(movieType), // plain text rendering form variable to template
"date" -> Text(dateOfHire), // plain text rendering form variable to template
"numberOfDays" -> Text(numberOfDays), // plain text rendering form variable to template
"regular" -> Text(discount), // plain text rendering form variable to template
"new" -> Text(""), // plain text rendering "" to template
"rtxt1" -> Text(""), // plain text rendering "" to template
"rtxt2" -> Text(""), // plain text rendering "" to template
"submit" -> SHtml.submit(submitLabel, () => S.mapSnippet("RentingForm.rent", editDetails _), "id" -> "submit")) // submit button
}
// definition for editDetails
def editDetails(xhtml: NodeSeq): NodeSeq = {
val discounts = SHtml.radio(discountMap.keys.toList, Full(discount), discount = _, "class" -> "required") // radio button
// Binding Form fields and components to template
bind("v", xhtml,
"name" -> SHtml.text(name, name = _, "class" -> "required"), // text field
"address" -> SHtml.textarea(address, address = _, "rows" -> "5", "class" -> "required"), // text area
"email" -> SHtml.text(email, email = _, "class" -> "required email"), // text field
"movie" -> SHtml.select(movieSeq, Full(movieType), movieType = _, "class" -> "required"), // select option combo box
"date" -> SHtml.text(dateOfHire, dateOfHire = _, "class" -> "required date", "id" -> "datePicker"), // text field
"numberOfDays" -> SHtml.text(numberOfDays, numberOfDays = _, "class" -> "required"), // text field
"regular" -> discounts(0), // radio button 0
"new" -> discounts(1), // radio button 1
"rtxt1" -> (discountMap.head.key), // radio button label 0
"rtxt2" -> (discountMap.last.key), // radio button label 1
"submit" -> SHtml.submit("Save", () => S.redirectTo("/view", () => S.mapSnippet("RentingForm.rent", thankYou _)), "id" -> "submit")) // submit button
}
// definition for thankYou
def thankYou(xhtml: NodeSeq): NodeSeq = {
Log.info(name, address, email, movieType, dateOfHire, numberOfDays, discount) // logging the form variables
// Binding Form fields and components to template
bind("v", xhtml,
"name" -> (name))
}
}
Finally, the corresponding web page for the above snippet, also notice that is wrapped around the with the which points to the template file in the “templates-hidden” folder. For more on this click here.
<lift:surround with="default" at="content">
<lift:RentingForm.rent form="POST" id="confirmform" class="cmxform">
<fieldset>
<legend>Video Hire Details</legend>
<div>
<label for="name">Name:<span>*</span></label>
<v:name/>
</div>
<div>
<label for="address">Address:<span>*</span></label>
<v:address/>
</div>
<div>
<label for="email">Email:<span>*</span></label>
<v:email/>
</div>
<div>
<label for="movietype">Movie Type:<span>*</span></label>
<v:movie/>
</div>
<div>
<label for="dateOfHire">Date of Hire:<span>*</span></label>
<v:date/>
<span id="notify"> DD/MM/YYYY </span>
</div>
<div>
<label for="numberOfDays">Number Of Days:<span>*</span></label>
<v:numberOfDays/>
</div>
<div class="radio">
<fieldset>
<legend><span> Discount Rate:<span>*</span> </span></legend>
<div>
<v:regular/><label> <v:rtxt1/> </label>
<v:new/> <label> <v:rtxt2/> </label>
</div>
</fieldset>
</div>
<div id="btn">
<v:submit/>
</div>
</fieldset>
</lift:RentingForm.rent>
</lift:surround>
For the full source code on this example:
git clone git@github.com:kengimel/Lift_forms.git
and also a working example here.
REFERENCE:
Odersky M. et al, 2008. Programming in Scala First Edition, Artima.
Pollak D, 2010 Scala Lift Off London, Q&A, Available at: http://skillsmatter.com/podcast/scala/scala-lift-off-david-pollak [Accessed November 2010.]
Wampler, D., 2008. Available at: http://blog.objectmentor.com/articles/2008/08/03/the-seductions-of-scala-part-i [Accessed January 2010.]