Let me also preface this that I
am writing this from memory and my own fairly strong opinions, so
Richard or anyone else, feel free to correct me. ;-)
Martin, Richard, thanks. To everyone, what do you think of the module
conventions that I outlined above? Would you like any changes (in
naming or initialization or anything else)? Am I missing something
that a module should do that we should standardize? Let's hammer this
out now so it doesn't bite us later.
--
You received this message because you are subscribed to the Google Groups "Lift" group.
To post to this group, send email to lif...@googlegroups.com.
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.
Lift has supported modules from the first version of the project in 2007. Lift's entire handling of the HTTP request/response cycle is open to hooks. Further, Lift's templating mechanism where resulting HTML pages are composed by transforming page content via snippets (See sec:Snippets) which are simply functions that take HTML and return HTML: NodeSeq => NodeSeq. Because Lift's snippet resolution mechanism is open and any code referenced in Boot (See sec:Boot), any code can be a Lift ``module'' by virtue of registering its snippets and other resources in LiftRules. Many Lift modules already exist including PayPal, OAuth, OpenID, LDAP, and even a module containing many jQuery widgets.
The most difficult issue relating to integration of external modules into Lift is how to properly insert the module's menu items into a SiteMap (See sec:SiteMap) menu hierarchy. Lift 2.2 introduces a more flexible mechanism for mutating the SiteMap: SiteMap mutators. SiteMap mutators are functions that rewrite the SiteMap based on rules for where to insert the module's menus in the menu hierarchy. Each module may publish markers. For example, here are the markers for ProtoUser:
1 2 3
4 5 6 7 8
9 10 11 12 13
14 15 16 | /** * Insert this LocParam into your menu if you want the * User's menu items to be inserted at the same level * and after the item */ final case object AddUserMenusAfter extends Loc.LocParam /** * replace the menu that has this LocParam with the User's menu * items */ final case object AddUserMenusHere extends Loc.LocParam[Any] /** * Insert this LocParam into your menu if you want the * User's menu items to be children of that menu */ final case object AddUserMenusUnder extends Loc.LocParam[Any] |
The module also makes a SiteMap mutator available, this can either be returned from the module's init method or via some other method on the module. ProtoUser makes the sitemapMutator method available which returns a SiteMap => SiteMap.
The application can add the marker to the appropriate menu item:
1 | Menu( "Home" ) / "index" » User.AddUserMenusAfter |
And when the application registers the SiteMap with LiftRules, it applies the mutator:
1 | LiftRules.setSiteMapFunc(() = > User.sitemapMutator(sitemap())) |
Because the mutators are composable:
1 2 | val allMutators = User.sitemapMutator andThen FruitBat.sitemapMutator LiftRules.setSiteMapFunc(() = > allMutators(sitemap())) |
For each module, the implementation of the mutators is pretty simple:
1 2 3
4 5 6 7 8
9 10 11 12 13
14 | private lazy val AfterUnapply = SiteMap.buildMenuMatcher( _ == AddUserMenusAfter) private lazy val HereUnapply = SiteMap.buildMenuMatcher( _ == AddUserMenusHere) private lazy val UnderUnapply = SiteMap.buildMenuMatcher( _ == AddUserMenusUnder) < p > /** * The SiteMap mutator function */ def sitemapMutator : SiteMap = > SiteMap = SiteMap.sitemapMutator case AfterUnapply(menu) = > menu :: sitemap case HereUnapply( _ ) = > sitemap case UnderUnapply(menu) = > List(menu.rebuild( _ ::: sitemap)) (SiteMap.addMenusAtEndMutator(sitemap)) < /p > |
We've defined some extractors that help with pattern matching. SiteMap.buildMenuMatcher
is a helper method to make building the extractors super-simple. Then
we supply a PartialFunction[Menu, List[Menu]] which
looks for the marker LocParam and re-writes the menu based
on the marker. If there are no matches, the additional rule is fired,
in this case, we append the menus at the end of the SiteMap.
I hope this leads to some excellent new modules.
--
You received this message because you are subscribed to the Google Groups "Lift" group.
To post to this group, send email to lif...@googlegroups.com.
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.
A related topic is how to build re-uasable components, like Snippets
that would be context-bounded by some states - or more like Snippets
local to other (parent) snippets, that have no self-being as a page[1].
Again, Lift already provides everything needed to build such components,
and again, it will be great to have general guide lines and convention
to make such component development less developer habits specific, at
least in their general APIs and life cycle.
For now, for my personal use, the convention are:
- these snippets don't go to the "snippet" package, but to a "component"
one ;
- they extends DispatchSnippet ;
- they are initialized and kept in their parent Snippet thanks to a
"LocalSnippet" contener (see code http://gist.github.com/729195 )
- they provide a "initJs()" method that manage all their JS stuff (it
happens quite often that these components are shown as results of AJAX
requests) ;
- they have a constructor with all their state configuration (context,
etc) ;
- they give back information to the calling snippet thanks to getter (I
mean, public def/val, not specifically "getSomeComponentState")
What are your opinions about that ? Is it something you also use ? If
not, how do you build your reusable tools in Lift ?
Cheers,
[1] typical example : a form used in several snippets, a JsTree or
datagrid integration, a pop-up, etc
--
Francois ARMAND
http://fanf42.blogspot.com
http://www.normation.com
Ah, just saw the thread about Wiring[1], and it's seems übber cool in
that context... Now, we could have complex components, wired in a parent
snippet, with updates on some leading to update on others...
I can't stop thinking to that use case: an embedable search component,
for example to search for emails:
You have a complex form component, build on two parts:
- the criteria definition part, an ajax form with a list of criterion
lines (type of criteria (from/to, date), comparator (is, contains, is
before), user input). You can add/remove lines, and there is a submit button
- a list of result, say a DataGrid integration (which would be an
other component) ;
Now, you could embed that search component on any pages... When you
update the search, automatically other states are updated. Things like
infos/actions: export to CSV, deletes all, etc
Well, I think I will have to look to that feature quickly...
and example: http://demo.liftweb.net/invoice_wiring
Todd
Well, that is not exactly as if other web framework didn't try and find
for us. Almost any component oriented frameworks like Tapestry 5, JSF
(yeah..), GWT, Wicket etc will have such library.
Examples:
- Vaadin: http://demo.vaadin.com/sampler
- IceFace:
http://component-showcase.icefaces.org/component-showcase/showcase.iface
- echo2/echo3: http://demo.nextapp.com/echo3csjs/
Etc.
So you often ends with:
- rich form inputs (autocompletion, date with calendar, slider,
two-columns multi-selects, etc)
- forms management (fields validation, state and error management, field
layout, etc)
- tree, grid, cover flow
- windows, pop-up, notification
- charts
- file upload & progress bar
- media (flash, quick time, html 5 video, google map, etc)
- secure transaction (paypal and other bank integratioon stuff)
And that covers the 80% of all element you need :)
Note: I'm not saying that Lift does not have any of that. For example,
LiftScreen are a really good candidate for form management :)