CSC/ECE 517 Fall 2014/ch1a 4 wl: Difference between revisions
(21 intermediate revisions by the same user not shown) | |||
Line 11: | Line 11: | ||
*Seaside’s highly granular sessions and security. | *Seaside’s highly granular sessions and security. | ||
*Rails’s fast flash-to-bang. | *Rails’s fast flash-to-bang. | ||
*Django’s “more than just CRUD is included”. | *[http://en.wikipedia.org/wiki/Django Django’s] “more than just [http://en.wikipedia.org/wiki/Create,_read,_update_and_delete CRUD] is included”. | ||
*Wicket’s designer-friendly templating style.<ref name=wiki>[https://www.assembla.com/spaces/liftweb/wiki/Home Lift Developers' wiki]</ref> | *Wicket’s designer-friendly templating style.<ref name=wiki>[https://www.assembla.com/spaces/liftweb/wiki/Home Lift Developers' wiki]</ref> | ||
Lift is built on top of the Scala programming language. Scala is a relatively new language which compiles to Java bytecode and runs on the JVM. Scala was developed by Martin Odersky. Since lift is built on Scala, the vast ecosystem of Java libraries can be leveraged for the web development like any other Java based web framework. Lift also uses the extensive XML library support and processing capabilities of the Scala language. | Lift is built on top of the Scala programming language. Scala is a relatively new language which compiles to Java bytecode and runs on the JVM. Scala was developed by Martin Odersky. Since lift is built on Scala, the vast ecosystem of Java libraries can be leveraged for the web development like any other Java based web framework. Lift also uses the extensive XML library support and processing capabilities of the Scala language. | ||
== The Scala Programming Language == | |||
[http://en.wikipedia.org/wiki/Scala_(programming_language) Scala] is a statically typed multi-paradigm programming language integrating features of object-oriented and functional languages. It runs on the JVM and is fully interoperable with Java. Scala is equipped with an expressive type system providing a powerful basis for the safe reuse of component abstractions and provides a combination of language mechanisms that make it easy to smoothly add new language constructs as libraries. The resulting language scalability -- as praised in Guy Steele's famous 1998 OOPSLA keynote, is illustrated by Scala's lightweight actor library. Furthermore, Scala syntax supports native XML literals. | |||
==Features of Lift Web Framework == | ==Features of Lift Web Framework == | ||
Line 21: | Line 25: | ||
* '''Parallel Page rendering:''' Executing multiple snippets in parallel is possible in Lift. Multiple jobs or processes can be spawned and run in parallel. However only when all the tasks or jobs are complete the final page render is sent back to the browser. | * '''Parallel Page rendering:''' Executing multiple snippets in parallel is possible in Lift. Multiple jobs or processes can be spawned and run in parallel. However only when all the tasks or jobs are complete the final page render is sent back to the browser. | ||
* '''Comet and Ajax support:''' Comet along with Ajax supports the asynchronous updates from user to the server or from server back to the user. | * '''[http://en.wikipedia.org/wiki/Comet_(programming) Comet] and [http://en.wikipedia.org/wiki/Ajax_(programming) Ajax] support:''' Comet along with Ajax supports the asynchronous updates from user to the server or from server back to the user. | ||
* '''Wiring:''' The relationship between different elements on a page is defined through Lift Wiring. When any element on a page changes, the dependent items are displayed on the next HTTP response. | * '''Wiring:''' The relationship between different elements on a page is defined through Lift Wiring. When any element on a page changes, the dependent items are displayed on the next HTTP response. | ||
Line 33: | Line 37: | ||
* ''' Modular:''' Lift apps can benefit from, easy to integrate, pre built modules<ref name=liftweb>[http://liftweb.net/ Lift Official HomePage]</ref> | * ''' Modular:''' Lift apps can benefit from, easy to integrate, pre built modules<ref name=liftweb>[http://liftweb.net/ Lift Official HomePage]</ref> | ||
== The View-First Approach == | == Comparison to Rails == | ||
=== The View-First Approach === | |||
Templates in Lift are used for display only and never contain programming logic. Consequently, they can be manipulated by non-programmers with tools such as Dreamweaver. Furthermore, contrarily to Rails, Lift snippets are invoked by the template, and not vice versa. The following template illustrates the view-first approach by invoking the form method in the User snippet using the <lift:snippet> tag. The body of this tag can refer to values bound by the User snippet in the snippet-defined fields namespace. | Templates in Lift are used for display only and never contain programming logic. Consequently, they can be manipulated by non-programmers with tools such as Dreamweaver. Furthermore, contrarily to Rails, Lift snippets are invoked by the template, and not vice versa. The following template illustrates the view-first approach by invoking the form method in the User snippet using the <lift:snippet> tag. The body of this tag can refer to values bound by the User snippet in the snippet-defined fields namespace. | ||
Line 52: | Line 58: | ||
Finally, the view-first approach allows the templates to easily glue different components together. However, when gluing components gets more complicated (e.g. when dependencies must be satisfied), the simplicity of this approach fails and actual code has to be written.<ref name=viewfirst>[http://stanford.wikia.com/wiki/Project_2:_Lift,_the_Scala_Web_framework View-First Approach]</ref> | Finally, the view-first approach allows the templates to easily glue different components together. However, when gluing components gets more complicated (e.g. when dependencies must be satisfied), the simplicity of this approach fails and actual code has to be written.<ref name=viewfirst>[http://stanford.wikia.com/wiki/Project_2:_Lift,_the_Scala_Web_framework View-First Approach]</ref> | ||
=== Long-Lived Closures === | |||
Lift allows the server application to attach closures to elements rendered in the user interface (e.g. form elements). The following snippet illustrates to usage of such closures. | |||
<pre> | |||
class User { | |||
def form(template: Group) = { | |||
var first, last = "" | |||
bind("fields", template, | |||
"first" --> text("", n => first = n), | |||
"last" --> text("", l => last = l), | |||
"submit" --> submit("Submit", ignore => process(first, last))) | |||
} | |||
def process(first: String, last: String) { | |||
... | |||
} | |||
} | |||
</pre> | |||
The n => first = n and l => last = l anonymous functions are attached to two text inputs. The input Lift passes to these functions is the value of the element. The body of these two functions assign the value of text input to the first and last variables. The ignore => process(first, last) anonymous function is attached to the submit button. In this case, we ignore the argument adequately named ignore because a button doesn't have a value. The body is call another function that could, for instance, store the two names in a database. | |||
To achieve this magic, Lift stores the closures in a map when the component is rendered. Randomly generated strings are used as keys and inserted in the generated HTML elements. When the form is submitted, Lift looks for the closures in the map based on the provided keys and executes them. Note how the first and last variables are captured by the three closures and, consequently, stored between the two request-response cycles. | |||
The advantage of this approach is that snippets can specify what happens when a specific button is pushed rather than having a controller parsing the submitted form and process the parameters extracted from the HTTP request. In essence, Lift allows you to specify server-side actions corresponding to user actions when the form is rendered rather than processing the results to figure out what actions the user performed during the next request. This reduces the need for magic constants used to map actions between the rendered view and controllers in other frameworks. In Rails, you might create a form with two buttons labeled 'Buy Now' and 'Buy Later' and then compare the submit parameter to 'Buy Now' and 'Buy Later' in the controller to determine the appropriate action. In Lift, this can be done with two different bindings in the render function.<ref name = closure>[http://stanford.wikia.com/wiki/Project_2:_Lift,_the_Scala_Web_framework Closures]</ref> | |||
== Lift Web Framework == | == Lift Web Framework == | ||
Line 61: | Line 92: | ||
## Rendering Pipeline | ## Rendering Pipeline | ||
## Invoking User Functions | ## Invoking User Functions | ||
# LiftRules : Lift Configuration | # LiftRules : Lift Configuration Management. Allows you to configure Lift. | ||
# LiftSession: Inherent Session State. | # LiftSession: Inherent Session State Representation.<ref name=liftsession>[http://exploring.liftweb.net/master/index-9.html#sec:Session-Management Session Management]</ref> | ||
# S: stateful context for request/response lifecycle. | # S: The stateful object impersonating the state context for a given request/response lifecycle.<ref name=s>[http://exploring.liftweb.net/master/index-9.html#sub:Advanced-S-Object S Object]</ref> | ||
# SiteMap: contains web pages for the lift application. | # SiteMap: contains web pages for the lift application.<ref name=sitemap>[http://exploring.liftweb.net/master/index-7.html#cha:SiteMap SiteMap]</ref> | ||
# SHtml: helper functions for XHtml. | # SHtml: helper functions for XHtml.<ref name=shtml>[http://exploring.liftweb.net/master/index-11.html#cha:AJAX-and-COMET SHtml]</ref> | ||
# Views: | # Views: LiftView objects impersonating a view as a XML content. Thus pages can be composed from other sources not only from html files.<ref name=views>[http://exploring.liftweb.net/master/index-4.html#sec:Views Lift Views]</ref> | ||
# LiftResponse: | # LiftResponse: Represents the abstraction of a response that will be propagated to the client. <ref name=response>[http://exploring.liftweb.net/master/index-9.html#sec:LiftResponse-in-Detail Lift Response]</ref> | ||
# Comet: allows sending asynchronous content to browser. | # Comet: allows sending asynchronous content to browser.<ref name =comet>[http://exploring.liftweb.net/master/index-11.html#sec:COMET Comet]</ref> | ||
# [http://en.wikipedia.org/wiki/Object-relational_mapping ORM]: | # [http://en.wikipedia.org/wiki/Object-relational_mapping ORM]: Either Mapper or Record - The lightweight ORM library provided by Lift. The Mapper framework is the proposed ORM framework for Lift 1.0 and the Record framework will be out for next releases. | ||
# HTTP Auth: provides control over authentication model. | # HTTP Auth: provides control over authentication model.<ref name=orm>[http://exploring.liftweb.net/master/index-8.html#cha:mapper_and_record HTTP Auth]</ref> | ||
# JS API: JavaScript abstraction layer.<ref name= | # JS API: The JavaScript abstraction layer. These are Scala classes/objects that abstract JavaScript artifacts. Such objects can be combined to build JavaScript code .<ref name=js>[http://exploring.liftweb.net/master/index-10.html#cha:Lift-and-Javascript JS]</ref> | ||
== Getting Started == | == Getting Started == | ||
Line 80: | Line 111: | ||
# Install plugin in Eclipse from this update site : http://download.scala-ide.org/sdk/helium/e38/scala211/stable/site | # Install plugin in Eclipse from this update site : http://download.scala-ide.org/sdk/helium/e38/scala211/stable/site | ||
# Once the plugin is installed restart Eclipse | # Once the plugin is installed restart Eclipse | ||
# Install sbteclipse by adding the following to projects/plugins.sbt in your Lift Project: <code> addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.5.0") </code | # Install sbteclipse<ref name=sbt>[http://en.wikipedia.org/wiki/SBT_(software) SBT]</ref> by adding the following to projects/plugins.sbt in your Lift Project: <code> addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.5.0") </code> | ||
# You can then create Eclipse project files (.project and .classpath) by entering the following into the SBT prompt: <code> eclipse </code><ref name=cookbook>[http://cookbook.liftweb.net/ The Lift Cookbook]</ref> | # You can then create Eclipse project files (.project and .classpath) by entering the following into the SBT prompt: <code> eclipse </code><ref name=cookbook>[http://cookbook.liftweb.net/ The Lift Cookbook]</ref> | ||
Line 138: | Line 169: | ||
} | } | ||
</pre><ref name=simplylift>[http://simply.liftweb.net/ Simply Lift]</ref> | </pre><ref name=simplylift>[http://simply.liftweb.net/ Simply Lift]</ref> | ||
== Framework Comparison == | |||
{| class="wikitable" | |||
|- | |||
! | |||
! Lift | |||
! Rails | |||
! Play | |||
|- | |||
| Scaffolding | |||
| No | |||
| Yes | |||
| Yes | |||
|- | |||
| Design Pattern | |||
| MVVM | |||
| MVC | |||
| MVC | |||
|- | |||
| Programming language | |||
| Scala | |||
| Ruby | |||
| Java | |||
|- | |||
| Template language | |||
| HTML5 | |||
| ERB/HAML | |||
| Groovy/Japid | |||
|- | |||
| Dynamic Typing | |||
| No | |||
| Yes | |||
| No | |||
|- | |||
| Admin Generator | |||
| Yes | |||
| Yes | |||
| No | |||
|} | |||
== Advantages == | == Advantages == | ||
Line 153: | Line 225: | ||
# Routing is a nightmare for no infrastructure is available. | # Routing is a nightmare for no infrastructure is available. | ||
# Despite the fact that we can't move logic from snippets to templates, it is possible to move XHTML from templates to snippets. It is not always clear where the view has to be. Is a snippet a controller or a dynamically generated view? Is there actually a difference between the two? | # Despite the fact that we can't move logic from snippets to templates, it is possible to move XHTML from templates to snippets. It is not always clear where the view has to be. Is a snippet a controller or a dynamically generated view? Is there actually a difference between the two? | ||
== Get Started == | |||
The Lift Cookbook is the best place to start with using the Lift Framework. | |||
[http://cookbook.liftweb.net/ CookBook] | |||
== References == | == References == | ||
<references></references> | <references></references> |
Latest revision as of 03:08, 26 September 2014
WEB DEVELOPMENT USING LIFT
Introduction
Lift is a web framework designed to make the most powerful techniques easily accessible while keeping the overall framework simple and flexible. Lift has picked some of the best ideas from a number of other frameworks while creating some of it's own novel ideas. This combination of solid foundation and new techniques makes Lift a powerful framework. The clear separation of presentation content and logic based on the concept of Model View Controller pattern is one of the key strengths of the framework.
"Lift is the kind of web framework that enables you as a developer to concentrate on the big picture. Strong, expressive typing and higher-level features like the built-in Comet support allow you to focus on innovating instead of the plumbing." -- Novell<ref name=overview>Lift Overview</ref>
Background
Lift is an open source software licensed under an Apache 2.0 license. Lift 2.5 is the latest stable version released and the Lift community has already started development for Lift 3.0. The source code can be found of the GitHub repository.<ref name=github>Lift Source Code</ref> Lift borrows from the best of existing frameworks, providing:
- Seaside’s highly granular sessions and security.
- Rails’s fast flash-to-bang.
- Django’s “more than just CRUD is included”.
- Wicket’s designer-friendly templating style.<ref name=wiki>Lift Developers' wiki</ref>
Lift is built on top of the Scala programming language. Scala is a relatively new language which compiles to Java bytecode and runs on the JVM. Scala was developed by Martin Odersky. Since lift is built on Scala, the vast ecosystem of Java libraries can be leveraged for the web development like any other Java based web framework. Lift also uses the extensive XML library support and processing capabilities of the Scala language.
The Scala Programming Language
Scala is a statically typed multi-paradigm programming language integrating features of object-oriented and functional languages. It runs on the JVM and is fully interoperable with Java. Scala is equipped with an expressive type system providing a powerful basis for the safe reuse of component abstractions and provides a combination of language mechanisms that make it easy to smoothly add new language constructs as libraries. The resulting language scalability -- as praised in Guy Steele's famous 1998 OOPSLA keynote, is illustrated by Scala's lightweight actor library. Furthermore, Scala syntax supports native XML literals.
Features of Lift Web Framework
- LazyLoading: Lift supports lazy loading of a snippet. This is useful when a snippet may take a long time to render, but it is required to return the page to the browser quickly.
- Parallel Page rendering: Executing multiple snippets in parallel is possible in Lift. Multiple jobs or processes can be spawned and run in parallel. However only when all the tasks or jobs are complete the final page render is sent back to the browser.
- Comet and Ajax support: Comet along with Ajax supports the asynchronous updates from user to the server or from server back to the user.
- Wiring: The relationship between different elements on a page is defined through Lift Wiring. When any element on a page changes, the dependent items are displayed on the next HTTP response.
- Security: Lift apps are not vulnerable to the common security concerns. Lift has built-in safeguards to combat vulnerabilities like Injection, XSS, Direct Object references and URL access
- Developer centric: Lift apps are fast to build, concise and easy to maintain and can be developed in totally designer friendly way.
- Scalable: Lift apps are high performance and scale in the real world to handle the high volume of traffic.
- Modular: Lift apps can benefit from, easy to integrate, pre built modules<ref name=liftweb>Lift Official HomePage</ref>
Comparison to Rails
The View-First Approach
Templates in Lift are used for display only and never contain programming logic. Consequently, they can be manipulated by non-programmers with tools such as Dreamweaver. Furthermore, contrarily to Rails, Lift snippets are invoked by the template, and not vice versa. The following template illustrates the view-first approach by invoking the form method in the User snippet using the <lift:snippet> tag. The body of this tag can refer to values bound by the User snippet in the snippet-defined fields namespace.
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:lift="http://liftweb.net/"> ... <lift:snippet type="user:form" form="POST"> First name <fields:first /> Last name <fields:last /> <fields:submit /> </lift:snippet> ... </html>
Rails' controller-first dispatch mechanism makes the assumption that there is only one piece of logic on the page and the rest is decoration. When a page contains more than one piece of logic (e.g. a shopping cart and an interactive chat), having to choose a single piece of logic make the controller code complicated. This usually require some combination of controller inheritance (move common logic into the superclass of controllers -- e.g. put the shopping cart code in the ApplicationController), moving code from controllers into helpers and models, and the use of partial views.
Finally, the view-first approach allows the templates to easily glue different components together. However, when gluing components gets more complicated (e.g. when dependencies must be satisfied), the simplicity of this approach fails and actual code has to be written.<ref name=viewfirst>View-First Approach</ref>
Long-Lived Closures
Lift allows the server application to attach closures to elements rendered in the user interface (e.g. form elements). The following snippet illustrates to usage of such closures.
class User { def form(template: Group) = { var first, last = "" bind("fields", template, "first" --> text("", n => first = n), "last" --> text("", l => last = l), "submit" --> submit("Submit", ignore => process(first, last))) } def process(first: String, last: String) { ... } }
The n => first = n and l => last = l anonymous functions are attached to two text inputs. The input Lift passes to these functions is the value of the element. The body of these two functions assign the value of text input to the first and last variables. The ignore => process(first, last) anonymous function is attached to the submit button. In this case, we ignore the argument adequately named ignore because a button doesn't have a value. The body is call another function that could, for instance, store the two names in a database.
To achieve this magic, Lift stores the closures in a map when the component is rendered. Randomly generated strings are used as keys and inserted in the generated HTML elements. When the form is submitted, Lift looks for the closures in the map based on the provided keys and executes them. Note how the first and last variables are captured by the three closures and, consequently, stored between the two request-response cycles.
The advantage of this approach is that snippets can specify what happens when a specific button is pushed rather than having a controller parsing the submitted form and process the parameters extracted from the HTTP request. In essence, Lift allows you to specify server-side actions corresponding to user actions when the form is rendered rather than processing the results to figure out what actions the user performed during the next request. This reduces the need for magic constants used to map actions between the rendered view and controllers in other frameworks. In Rails, you might create a form with two buttons labeled 'Buy Now' and 'Buy Later' and then compare the submit parameter to 'Buy Now' and 'Buy Later' in the controller to determine the appropriate action. In Lift, this can be done with two different bindings in the render function.<ref name = closure>Closures</ref>
Lift Web Framework
Components
- LiftCore: This is the web processor of the framework which handles the following functions.
- Request/Response
- Rendering Pipeline
- Invoking User Functions
- LiftRules : Lift Configuration Management. Allows you to configure Lift.
- LiftSession: Inherent Session State Representation.<ref name=liftsession>Session Management</ref>
- S: The stateful object impersonating the state context for a given request/response lifecycle.<ref name=s>S Object</ref>
- SiteMap: contains web pages for the lift application.<ref name=sitemap>SiteMap</ref>
- SHtml: helper functions for XHtml.<ref name=shtml>SHtml</ref>
- Views: LiftView objects impersonating a view as a XML content. Thus pages can be composed from other sources not only from html files.<ref name=views>Lift Views</ref>
- LiftResponse: Represents the abstraction of a response that will be propagated to the client. <ref name=response>Lift Response</ref>
- Comet: allows sending asynchronous content to browser.<ref name =comet>Comet</ref>
- ORM: Either Mapper or Record - The lightweight ORM library provided by Lift. The Mapper framework is the proposed ORM framework for Lift 1.0 and the Record framework will be out for next releases.
- HTTP Auth: provides control over authentication model.<ref name=orm>HTTP Auth</ref>
- JS API: The JavaScript abstraction layer. These are Scala classes/objects that abstract JavaScript artifacts. Such objects can be combined to build JavaScript code .<ref name=js>JS</ref>
Getting Started
Set Up on Eclipse
- Download “Scala IDE for Eclipse” : http://scala-ide.org/
- Install plugin in Eclipse from this update site : http://download.scala-ide.org/sdk/helium/e38/scala211/stable/site
- Once the plugin is installed restart Eclipse
- Install sbteclipse<ref name=sbt>SBT</ref> by adding the following to projects/plugins.sbt in your Lift Project:
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.5.0")
- You can then create Eclipse project files (.project and .classpath) by entering the following into the SBT prompt:
eclipse
<ref name=cookbook>The Lift Cookbook</ref>
Setup Video Tutorial : link
Basic Web Application Structure
Step 1: Making a SiteMap entry
Every page on the site needs a SiteMap entry.
def sitemap(): SiteMap = SiteMap( Menu("Home") / "index", Menu("Second Page") / "second" )
Step 2: Creating the view
Create an HTML file that corresponds to the sitemap entry.
<meta content="text/html; charset=UTF-8" http-equiv="content-type"> <title>Home</title> <div id="main" class="lift:surround?with=default&at=content"> <div> Hi, I'm a page that contains the time: <span class="lift:TimeNow">??? some time</span>. </div> <div> And a button: <button class="lift:ClickMe">Click Me</button>. </div> </div>
Step 3 : Creating the Snippet
A snippet can be thought of as a controller which has rules for transforming the section of your template.
package code package snippet import net.liftweb._ import util._ import Helpers._ object TimeNow { def render = "* *" #> now.toString }
<ref name=simplylift>Simply Lift</ref>
Framework Comparison
Lift | Rails | Play | |
---|---|---|---|
Scaffolding | No | Yes | Yes |
Design Pattern | MVVM | MVC | MVC |
Programming language | Scala | Ruby | Java |
Template language | HTML5 | ERB/HAML | Groovy/Japid |
Dynamic Typing | No | Yes | No |
Admin Generator | Yes | Yes | No |
Advantages
- Binding server side state to users' sessions is incredibly powerful. Each user can have a living representation in the server that talks to other users' representations.<ref name=adv>Lift Project</ref>
- Built-in Comet support possibly the best Comet support in web frameworks. Pushing data to the browser is as simple as calling partialUpdate or asking for a full re-rendering of the component. Updates of multiple components are automatically serialized in a unique connection.
- Long-live closures simplify form processing and can be used to create complex wizards.
- View-first approach helps with 'Lean Controller, Fat Model'.
- Server state machines support for models, including timeouts. For instance, we can use a machine to specify that after three days without confirmation, a new account has to be deleted. Consequently, there is no need for cron and script/runner type solutions in Rails. Instead of imperatively stating what maintenance operations to do; state what rules to enforce and it is automatically done when necessary.
Disadvantages
- Moving state to the server comes with all disadvantages discussed in class. That being said, various remote actors libraries are currently being developed and would permit actors living in different JVMs (running possibly on different machines) to communicate between each others seamlessly. <ref name=adv>Lift Project</ref>Furthermore, it is often possible to architect an application such that loosely coupled components can live on different machines -- it is definitely possible with the stock trading application we will present during our in-class presentation.
- Terrible API (i.e. badly chosen names, confusing domain model) and no documentation at all (the Scala documentation is OK, though).
- Despite the fact that the API is stabilizing (unfortunately?), most of the tutorials are outdated.
- Routing is a nightmare for no infrastructure is available.
- Despite the fact that we can't move logic from snippets to templates, it is possible to move XHTML from templates to snippets. It is not always clear where the view has to be. Is a snippet a controller or a dynamically generated view? Is there actually a difference between the two?
Get Started
The Lift Cookbook is the best place to start with using the Lift Framework. CookBook
References
<references></references>