cwvreg.py
branchstable
changeset 5147 70181998897f
parent 5143 43afbdd5c8b4
child 5174 78438ad513ca
child 5273 c4caef6f09c9
--- a/cwvreg.py	Tue Apr 06 10:10:47 2010 +0200
+++ b/cwvreg.py	Tue Apr 06 10:11:40 2010 +0200
@@ -1,9 +1,178 @@
-"""extend the generic VRegistry with some cubicweb specific stuff
+# :organization: Logilab
+# :copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+# :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+# :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+""".. VRegistry:
+
+The `VRegistry`
+---------------
+
+The `VRegistry` can be seen as a two level dictionary. It contains all objects
+loaded dynamically to build a |cubicweb| application. Basically:
+
+* first level key return a *registry*. This key corresponds to the `__registry__`
+  attribute of application object classes
+
+* second level key return a list of application objects which share the same
+  identifier. This key corresponds to the `__regid__` attribute of application
+  object classes.
+
+A *registry* hold a specific kind of application objects. You've for instance
+a registry for entity classes, another for views, etc...
+
+The `VRegistry` has two main responsibilities:
+
+- being the access point to all registries
+
+- handling the registration process at startup time, and during automatic
+  reloading in debug mode.
+
+
+.. _AppObjectRecording:
+
+Details of the recording process
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. index::
+   vregistry: registration_callback
+
+On startup, |cubicweb| have to load application objects defined in its library
+and in cubes used by the instance. Application objects from the library are
+loaded first, then those provided by cubes are loaded in an ordered way (e.g. if
+your cube depends on an other, objects from the dependancy will be loaded
+first). Cube's modules or packages where appobject are looked at is explained in
+:ref:`cubelayout`.
+
+For each module:
+
+* by default all objects are registered automatically
+
+* if some objects have to replace other objects, or be included only if some
+  condition is true, you'll have to define a `registration_callback(vreg)`
+  function in your module and explicitly register **all objects** in this module,
+  using the api defined below.
+
+.. Note::
+    Once the function `registration_callback(vreg)` is implemented in a module,
+    all the objects from this module have to be explicitly registered as it
+    disables the automatic objects registration.
+
+
+API for objects registration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Here are the registration methods that you can use in the `registration_callback`
+to register your objects to the `VRegistry` instance given as argument (usually
+named `vreg`):
+
+.. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register_all
+.. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register_and_replace
+.. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register
+.. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register_if_interface_found
+.. automethod:: cubicweb.cwvreg.CubicWebVRegistry.unregister
+
+
+Examples:
+
+.. sourcecode:: python
+
+   # web/views/basecomponents.py
+   def registration_callback(vreg):
+      # register everything in the module except SeeAlsoComponent
+      vreg.register_all(globals().values(), __name__, (SeeAlsoVComponent,))
+      # conditionally register SeeAlsoVComponent
+      if 'see_also' in vreg.schema:
+          vreg.register(SeeAlsoVComponent)
 
-:organization: Logilab
-:copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
-:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
-:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+In this example, we register all application object classes defined in the module
+except `SeeAlsoVComponent`. This class is then registered only if the 'see_also'
+relation type is defined in the instance'schema.
+
+.. sourcecode:: python
+
+   # goa/appobjects/sessions.py
+   def registration_callback(vreg):
+      vreg.register(SessionsCleaner)
+      # replace AuthenticationManager by GAEAuthenticationManager
+      vreg.register_and_replace(GAEAuthenticationManager, AuthenticationManager)
+      # replace PersistentSessionManager by GAEPersistentSessionManager
+      vreg.register_and_replace(GAEPersistentSessionManager, PersistentSessionManager)
+
+In this example, we explicitly register classes one by one:
+
+* the `SessionCleaner` class
+* the `GAEAuthenticationManager` to replace the `AuthenticationManager`
+* the `GAEPersistentSessionManager` to replace the `PersistentSessionManager`
+
+If at some point we register a new appobject class in this module, it won't be
+registered at all without modification to the `registration_callback`
+implementation. The previous example will register it though, thanks to the call
+to the `register_all` method.
+
+
+.. _Selection:
+
+Runtime objects selection
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Now that we've all application objects loaded, the question is : when I want some
+specific object, for instance the primary view for a given entity, how do I get
+the proper object ? This is what we call the **selection mechanism**.
+
+As explained in the :ref:`Concepts` section:
+
+* each application object has a **selector**, defined by its `__select__` class attribute
+
+* this selector is responsible to return a **score** for a given context
+
+  - 0 score means the object doesn't apply to this context
+
+  - else, the higher the score, the better the object suits the context
+
+* the object with the higher score is selected.
+
+.. Note::
+
+  When no score is higher than the others, an exception is raised in development
+  mode to let you know that the engine was not able to identify the view to
+  apply. This error is silenced in production mode and one of the objects with
+  the higher score is picked.
+
+  In such cases you would need to review your design and make sure your selectors
+  or appobjects are properly defined.
+
+For instance, if you are selecting the primary (eg `__regid__ = 'primary'`) view (eg
+`__registry__ = 'views'`) for a result set containing a `Card` entity, 2 objects
+will probably be selectable:
+
+* the default primary view (`__select__ = implements('Any')`), meaning
+  that the object is selectable for any kind of entity type
+
+* the specific `Card` primary view (`__select__ = implements('Card')`,
+  meaning that the object is selectable for Card entities
+
+Other primary views specific to other entity types won't be selectable in this
+case. Among selectable objects, the implements selector will return a higher
+score than the second view since it's more specific, so it will be selected as
+expected.
+
+.. _SelectionAPI:
+
+API for objects selections
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Here is the selection API you'll get on every registry. Some of them, as the
+'etypes' registry, containing entity classes, extend it. In those methods,
+`*args, **kwargs` is what we call the **context**. Those arguments are given to
+selectors that will inspect there content and return a score accordingly.
+
+.. automethod:: cubicweb.vregistry.Registry.select
+
+.. automethod:: cubicweb.vregistry.Registry.select_or_none
+
+.. automethod:: cubicweb.vregistry.Registry.possible_objects
+
+.. automethod:: cubicweb.vregistry.Registry.object_by_id
 """
 __docformat__ = "restructuredtext en"
 _ = unicode