All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.liftweb.http.Bindings.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2006-2009 WorldWide Conferencing, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 */

package net.liftweb
package http 

import common.{Box,Full,Empty,Failure}
import util.Props
import scala.xml.{NodeSeq, Text}

/**
 * A collection of types and implicit transformations used to allow composition
 * of page elements based upon the types of rendered objects.
 *
 * In Lift, a "snippet" is a function from NodeSeq => NodeSeq, where the argument
 * to the function is a template, and the result is a fragment of a page to be 
 * rendered. Of course, this is a bit of an abbreviation; the snippet function
 * also has an argument which is the application state made available from S.
 * A DataBinding[T] is very similar in this respect; it is a function from some
 * piece of information of type T to a function from NodeSeq => NodeSeq. Since
 * DataBinding is strongly typed with respect to the type of information being
 * rendered, DataBinding instances are ideal for the rendering of objects that
 * is used to build up snippets. For example:
 * 
 * 
 * import net.liftweb.http.Bindings._

 * case class MyClass(str: String, i: Int, other: MyOtherClass)
 * case class MyOtherClass(foo: String)
 *
 * trait MyClassBinding extends DataBinding[MyClass] {
 *   implicit val otherBinding: DataBinding[MyOtherClass]
 *
 *   override def apply(entity: MyClass) = (xhtml: NodeSeq) => {
 *     val otherTemplate = chooseTemplate("myclass", "other", xhtml)
 *     bind(
 *       "myclass", xhtml, 
 *       "str" -> Text("#" + entity.str + "#"),
 *       "i" -> Text(entity.i.toString),
 *       "other" -> entity.other.bind(otherTemplate)
 *     )
 *   }
 *
 * }
 * 
 * object myOtherClassBinding extends DataBinding[MyOtherClass] {
 *   override def apply(other: MyOtherClass) = (xhtml: NodeSeq) => {
 *     bind("other", xhtml, "foo" -> Text("%" + other.foo + "%"))
 *   }
 * }
 *
 * object MyClassConcreteBinding extends MyClassBinding {
 *   override val otherBinding = myOtherClassBinding
 * }
 * 
* * In this example, two classes and their associated bindings are constructed; * the first binding for MyClass is abstract, needing a specific instance of * DataBinding[MyOtherClass] to enable the implicit conversion needed to render * the contained MyOtherClass instance. A subtemplate is selected, and the * call to other.bind both necessitates the implicit conversion to a Bindings.Binder * instance and applies the appropriate formatting. You can see how this * usage keeps the concerns of the view and the model nicely separated, while * allowing composition over object graphs. * * Please see the tests, as well as this blog post for additional details. */ object Bindings { type Binding = NodeSeq => NodeSeq type DataBinding[T] = T => NodeSeq => NodeSeq /** * Implicitly convert the specified object to a binder for that object if a DataBinding for * that object's type is available in implicit scope. This essentially adds a bind() method * to an object if an appropriate implicit DataBinding is available. */ implicit def binder[T](t: T)(implicit binding: DataBinding[T]): Binder = Binder(binding(t)) /** * Wrap the specified Binding (a function from NodeSeq => NodeSeq) in a Binder so that * it can be applied using Binder's bind methods. */ implicit def binder(binding: Binding): Binder = Binder(binding) /** * A decorator for a binding function that allows it to be called as bind() rather than apply(). * This class also provides facilities for binding to a specific template */ case class Binder(val binding: Binding) { /** * Apply this binder's binding function to the specified NodeSeq. */ def bind(xhtml: NodeSeq): NodeSeq = binding.apply(xhtml) /** * Apply this binder's binding function to the specified templated * looked up using Templates.apply */ def bind(templatePath: List[String]): NodeSeq = { Templates(templatePath) map binding match { case Full(xhtml) => xhtml case Failure(msg, ex, _) if Props.mode == Props.RunModes.Development => Text(ex.map(_.getMessage).openOr(msg)) case Empty if Props.mode == Props.RunModes.Development => Text("Unable to find template with path " + templatePath.mkString("/", "/", "")) case _ => NodeSeq.Empty } } } /** * Bind any input value to the empty NodeSeq. */ object EmptyBinding extends Binding { override def apply(xhtml : NodeSeq) : NodeSeq = NodeSeq.Empty } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy