Forum OpenACS Improvement Proposals (TIPs): TIP#120 (Approved) Make package types object types
I propose creating an acs_object_type for each package type as a subtype of apm_package.
This would allow for a hierarchy of package types. The most common example we might think of is dotlrn. The dotlrn package could be a subtype of the subsite package. This would allow an easier way to determine if a package takes on the role of a subsite type package and it could greatly simplify all the code that has to be special-cased to determine if dotlrn is installed.
This would also allow simplification of the case when a new package is created to customize the user interface of another package. For example, many people would like to customize the forums package, to change the style of user interface. If we had a custom-forums packge type that was subtyped from forums, we could have a simple and consistent mechnism to defer pages to the parent package type for handling. This would allow much easier reuse of pages and would make it very simple for local customizations to be handled safely without worrying about accidentally adding custom code back into OpenACS.
The immediate goal of this work is to make the dotlrn package a subtype of the acs-subsite package. I have done preliminary work to turn dotlrn communities (groups) into application groups of the dotlrn package also. This should make it easier to do an upgrade to an existing dotlrn install that will support subsite based dotlrn.
Previous work includes the http://openacs.org/api-doc/proc-view?proc=subsite%3a%3apackage%5fkeys subsite::package_keys procedure. This basically is a hard coded list of packages that fufuill the subsite role. Defining children of a subsite object_type would allow greatly flexibility and use the existing features of the toolkit.
Steps to implement
1) Make a new object_type for existing package_types when a new package type is created. Default is subtype of apm_package.
2) Add parameter to package create code to specify package supertype
3) Add tag to info file supertype which is optional and will default to apm_package. This tag will have the same behavior as requires element so tcl libraries are loaded of the supertype.
4) Add attribute to apm_package_types for inherit_ui_p to specify if URL resolution will go up the package type hierarchy.
5) Update apm_paramaters to support setting parameters for the supertype package parameters up the package type hierarchy. Allow subtyped packages to override the default value of an inherited parameter.
6) Fix package loading to load tcl libraries in package dependency order.
See previous discussion
Also, package semantics are such that it's not clear this is the best approach.
We already have "PROVIDES" and "REQUIRES" dependency information being kept for packages. Basing package extension by making packages true acs object types would not mesh cleanly with the existing dependency specifications.
In my implementation, I decided to implement package extension through the dependency information that's already available, by adding a new dependency type "EXTENDS".
This has the advantage of being very easy to implement, including upgrade scripts, plus it fits naturally into the dependency scheme for packages that already exists.
Also, unlike with acs object types, the implementation easily allows for "multiple extending" (package inheritance), which I've already found to be extremely useful to knit together two or more packages into a coherent whole.
I've also implemented a top-level (i.e. like "singleton-p") package attribute "inherit-templates-p", which implements the concept outlined in point #4 of Dave's original list.
So, for instance, to build subsites using the layout manager package recently committed to HEAD, you can write a package consisting of the following .info file snippet:
<provides name="layout-managed-subsite" ...>
<extends name="layout-manager" ...>
<extends name="layout-manager" ...>
<extends name="acs-subsite" ...>
and presto, off you go.
WHY DO WE WANT THIS:
It makes maintenance of custom versions of packages that make relatively minor changes or enhancements much easier. You don't have to modify the base package's source at all, and can easily update from the OpenACS.org CVS tree (of course, your package may not work correctly afterwards, which is true today for packages yours "requires"). It makes clear what's been customized because all of your customizations, including API enhancements, database extensions, as well as new or customized scripts can all be placed in a separate package.
This also greatly reduces the need to mount packages instances just so another package that requires it has a URL to reference for to serve pages from that package. A UI enhancement package like the ajax file storage package can simply extend its base package. Mounting the extended package essentially instantiates a file storage instance (sharing the extending package's package_id).
Since base packages share mount points and package_ids with the extending package, there's no need to mount or find the base package (file storage in the Ajax fs ui case).
More directories need to be searched by the request processor when operating in normal mode. This adds a small amount of overhead to request processing time. On the other hand, implementing this required me to visit, understand, and finally substantially simplify, speed up, and rewrite that part of the request processor that finds an appropriate .vuh file to serve if an exact file match cannot be found.
In performance mode (the one that counts), URL-to-file mappings are cached anyway, so this minor performance hit only has to be paid for the first visit.
There's also a slight performance hit during restart, as the NSV array that contains the URL-to-file search order for each package_key is built during startup.
The recursive set of "extends" and "requires" results in a DAG rooted in the current package when graphed such that each package only occurs once in the graph. The simple algorithm to build the URL search order from this abstract graph is very similar to that required correctly call APM callbacks when a package is mounted, etc. Indeed, if "inherit-templates-p" is true, the resulting ordered lists of packages are simply the reverse of each other.
This same recursive walk algorithm is used to compute the correct order of Tcl library sourcing for each package.
Currently during startup packages have their Tcl libraries sourced in ascending alphabetical order, which only works because packages rarely have dependencies on each other in their init files.
Future work, then, includes sourcing package Tcl libraries in correct order based on their "extends" and "requires" dependency hierarchies.
1. new dependency type "extends"
2. migration of parameters to packages that extend others
3. package instantiation, mount, unmount etc with callbacks executed in the proper order.
4. APM UI changes to allow the specification, display and editing of the new attributes.
5. request processor changes
The semantics seem right to me, daveb has a copy of the code and will be reviewing soon. After review, I propose committing the code and making examples available, and updating documentation to reflect the new capabilitles.
I think other OCT would like to check the code as well (including me).