23.1 Snippet Resolution
Lift snippets transform markup to dynamic content. The are functions that transform NodeSeq => NodeSeq.
Snippets can be invoked from templates via tags:
<lift:surround with="default" at="content">
<p>
You have reached this page, but you can only get here if you’ve logged in
first.
</p>
</lift:surround>
or via class attributes.
<p class="lift:surround?with=default;at=content">
You have reached this page, but you can only get here if you’ve logged in
first.
</p>
In both cases, the surround (See
↑) snippet will be invoked with attribute
with set to
default and
at set to
content. The parameter passed to the surround
NodeSeq => NodeSeq function is:
<p>
You have reached this page, but you can only get here if you’ve logged in
first.
</p>
Lift will resolve from the snippet name to a function in the following steps.
23.1.1 LiftSession.liftTagProcessing
Lift consults a List[PartialFunction[(String, Elem, MetaData, NodeSeq, String), NodeSeq]] located in LiftSession.liftTagProcessing for the rules to use to evaluate the snippet name, attributes, etc. into the resulting NodeSeq. LiftSession.liftTagProcessing is the result of LiftRules.liftTagProcessing or else the default Lift tag processor. If you need special snippet resolution mechanisms, you can place them in LiftRules.liftTagProcessing. By default, the snippets get processed by LiftSession.processSnippet.
23.1.2 LiftRules.liftTagProcessing
LiftRules.liftTagProcessing looks for the form attribute and sets the isForm variable. Next, Lift determines if the contents of the snippet should be evaluated eagerly by looking for one of eager_eval, l:eager_eval, or lift:eager_eval attributes.
If the snippet is an eager evaluation, the child tags will be evaluated for any snippets.
Either the originally passed children or the eagerly evaluated children will be referred to as children in the next section.
23.1.3 Snippet name resolution
Lift looks for the named snippet in the following locations in order:
-
S.locateMappedSnippet - the complete snippet name without any camel or snake application is used to look up a NodeSeq => NodeSeq in within the scope of the current extended request. Snippets may be registered using S.mapSnippet.
-
SiteMap Loc snippet - the current SiteMap Loc (S.location) will be queried to see if it has a NodeSeq => NodeSeq that matches the current snippet name (loc.snippet(snippetName)).
-
LiftRules.snippets - next, the snippet name is split at the ’.’ character to determine the snippet name and snippet method name. The snippets RulesSeq is tested for a match between the List[String] that results from splitting the name at the period and NodeSeq => NodeSeq.
-
If the above mechanisms do not result in a NodeSeq => NodeSeq, Lift looks for a Class that matches the name.
-
S.snippetForClass - is checked to see if a Class has been associated with the snippet name. If none is found...
-
LiftRules.snippetDispatch is checked to see if theres an instance of DispatchSnippet that matches to snippet name. Lift’s built-in snippets are registered with LiftRules.snippetDispatch. If there’s no match...
-
Lift tries reflection to find a matching class name (note that Lift will try camel case and snake case for class names, so the foo_bar snippet will match the class foo_bar as well as FooBar). Lift looks for classes in the snippet subpackage of all the packages added via LiftRules.addToPackages. So if you call LiftRules.addToPackages("foo.bar") in Boot.scala, then Lift will search for the classes foo.bar.snippet.foo_bar and foo.bar.snippet.FooBar.
-
Once the class is found, Lift will try to instantiate the class the following ways:
-
Lift will look at the current location (S.location) and if the parameter type of the Loc is not Unit, Lift get the current parameter and look for a constructor that matches the current parameter type or Box of current parameter type (and superclasses of both). If there’s a match the constructor will be called with the parameters. For example, if the current page is a Loc[Dog] and Dog is a subclass of Animal, the following constructors will match:
-
class MySnippet(dog: Dog)
-
class MySnippet(animal: Animal)
-
class MySnippet(dog: Box[Dog])
-
class MySnippet(animal: Box[Animal])
-
class MySnippet(dog: Dog, session: LiftSession)
-
class MySnippet(animal: Animal, session: LiftSession)
-
class MySnippet(dog: Box[Dog], session: LiftSession)
-
class MySnippet(animal: Box[Animal], session: LiftSession)
-
If a typed constructor cannot be found, try the zero argument constructor;
-
If the zero argument constructor cannot be found, try to treat the Class as a Scala object singleton and get the instance that the singleton refers to.
-
Once we’ve got an instance of the potential snippet handling class:
-
If it’s a StatefulSnippet, register with S.overrideSnippetForClass;
-
Update the LiftSession snippetMap RequestVar so subsequent references to the snippet during the same extended request uses same instance (that way if any instance variables are set on the class instance, they are picked up by subsequent accesses to the same snippet);
-
Next, Lift attempts to invoke the snippet method. If no explicit method is given, the render method is used.
-
Stateful and Dispatch use dispatch method to find the NodeSeq => NodeSeq
-
Non-dispatch, do the following method lookup:
-
method that takes no parameters and returns CssBindFunc, NodeSeq => NodeSeq, invoke the method and apply the function to the children; or
-
try to invoke the named method with Group(children) (NodeSeq signature) or invoke it with no parameters. If the return value is NodeSeq, Node, or Seq[Node], then it was successful.
23.1.4 Post-processing of results
-
LiftRules.snippetDispatch (built in snippets registered here)
parallel snippets
(C) 2012 David Pollak