1 """extend the generic VRegistry with some cubicweb specific stuff |
1 # :organization: Logilab |
2 |
2 # :copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2. |
3 :organization: Logilab |
3 # :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
4 :copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2. |
4 # :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses |
5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
5 """.. VRegistry: |
6 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses |
6 |
|
7 The `VRegistry` |
|
8 --------------- |
|
9 |
|
10 The `VRegistry` can be seen as a two level dictionary. It contains all objects |
|
11 loaded dynamically to build a |cubicweb| application. Basically: |
|
12 |
|
13 * first level key return a *registry*. This key corresponds to the `__registry__` |
|
14 attribute of application object classes |
|
15 |
|
16 * second level key return a list of application objects which share the same |
|
17 identifier. This key corresponds to the `__regid__` attribute of application |
|
18 object classes. |
|
19 |
|
20 A *registry* hold a specific kind of application objects. You've for instance |
|
21 a registry for entity classes, another for views, etc... |
|
22 |
|
23 The `VRegistry` has two main responsibilities: |
|
24 |
|
25 - being the access point to all registries |
|
26 |
|
27 - handling the registration process at startup time, and during automatic |
|
28 reloading in debug mode. |
|
29 |
|
30 |
|
31 .. _AppObjectRecording: |
|
32 |
|
33 Details of the recording process |
|
34 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
35 |
|
36 .. index:: |
|
37 vregistry: registration_callback |
|
38 |
|
39 On startup, |cubicweb| have to load application objects defined in its library |
|
40 and in cubes used by the instance. Application objects from the library are |
|
41 loaded first, then those provided by cubes are loaded in an ordered way (e.g. if |
|
42 your cube depends on an other, objects from the dependancy will be loaded |
|
43 first). Cube's modules or packages where appobject are looked at is explained in |
|
44 :ref:`cubelayout`. |
|
45 |
|
46 For each module: |
|
47 |
|
48 * by default all objects are registered automatically |
|
49 |
|
50 * if some objects have to replace other objects, or be included only if some |
|
51 condition is true, you'll have to define a `registration_callback(vreg)` |
|
52 function in your module and explicitly register **all objects** in this module, |
|
53 using the api defined below. |
|
54 |
|
55 .. Note:: |
|
56 Once the function `registration_callback(vreg)` is implemented in a module, |
|
57 all the objects from this module have to be explicitly registered as it |
|
58 disables the automatic objects registration. |
|
59 |
|
60 |
|
61 API for objects registration |
|
62 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
63 |
|
64 Here are the registration methods that you can use in the `registration_callback` |
|
65 to register your objects to the `VRegistry` instance given as argument (usually |
|
66 named `vreg`): |
|
67 |
|
68 .. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register_all |
|
69 .. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register_and_replace |
|
70 .. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register |
|
71 .. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register_if_interface_found |
|
72 .. automethod:: cubicweb.cwvreg.CubicWebVRegistry.unregister |
|
73 |
|
74 |
|
75 Examples: |
|
76 |
|
77 .. sourcecode:: python |
|
78 |
|
79 # web/views/basecomponents.py |
|
80 def registration_callback(vreg): |
|
81 # register everything in the module except SeeAlsoComponent |
|
82 vreg.register_all(globals().values(), __name__, (SeeAlsoVComponent,)) |
|
83 # conditionally register SeeAlsoVComponent |
|
84 if 'see_also' in vreg.schema: |
|
85 vreg.register(SeeAlsoVComponent) |
|
86 |
|
87 In this example, we register all application object classes defined in the module |
|
88 except `SeeAlsoVComponent`. This class is then registered only if the 'see_also' |
|
89 relation type is defined in the instance'schema. |
|
90 |
|
91 .. sourcecode:: python |
|
92 |
|
93 # goa/appobjects/sessions.py |
|
94 def registration_callback(vreg): |
|
95 vreg.register(SessionsCleaner) |
|
96 # replace AuthenticationManager by GAEAuthenticationManager |
|
97 vreg.register_and_replace(GAEAuthenticationManager, AuthenticationManager) |
|
98 # replace PersistentSessionManager by GAEPersistentSessionManager |
|
99 vreg.register_and_replace(GAEPersistentSessionManager, PersistentSessionManager) |
|
100 |
|
101 In this example, we explicitly register classes one by one: |
|
102 |
|
103 * the `SessionCleaner` class |
|
104 * the `GAEAuthenticationManager` to replace the `AuthenticationManager` |
|
105 * the `GAEPersistentSessionManager` to replace the `PersistentSessionManager` |
|
106 |
|
107 If at some point we register a new appobject class in this module, it won't be |
|
108 registered at all without modification to the `registration_callback` |
|
109 implementation. The previous example will register it though, thanks to the call |
|
110 to the `register_all` method. |
|
111 |
|
112 |
|
113 .. _Selection: |
|
114 |
|
115 Runtime objects selection |
|
116 ~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
117 |
|
118 Now that we've all application objects loaded, the question is : when I want some |
|
119 specific object, for instance the primary view for a given entity, how do I get |
|
120 the proper object ? This is what we call the **selection mechanism**. |
|
121 |
|
122 As explained in the :ref:`Concepts` section: |
|
123 |
|
124 * each application object has a **selector**, defined by its `__select__` class attribute |
|
125 |
|
126 * this selector is responsible to return a **score** for a given context |
|
127 |
|
128 - 0 score means the object doesn't apply to this context |
|
129 |
|
130 - else, the higher the score, the better the object suits the context |
|
131 |
|
132 * the object with the higher score is selected. |
|
133 |
|
134 .. Note:: |
|
135 |
|
136 When no score is higher than the others, an exception is raised in development |
|
137 mode to let you know that the engine was not able to identify the view to |
|
138 apply. This error is silenced in production mode and one of the objects with |
|
139 the higher score is picked. |
|
140 |
|
141 In such cases you would need to review your design and make sure your selectors |
|
142 or appobjects are properly defined. |
|
143 |
|
144 For instance, if you are selecting the primary (eg `__regid__ = 'primary'`) view (eg |
|
145 `__registry__ = 'views'`) for a result set containing a `Card` entity, 2 objects |
|
146 will probably be selectable: |
|
147 |
|
148 * the default primary view (`__select__ = implements('Any')`), meaning |
|
149 that the object is selectable for any kind of entity type |
|
150 |
|
151 * the specific `Card` primary view (`__select__ = implements('Card')`, |
|
152 meaning that the object is selectable for Card entities |
|
153 |
|
154 Other primary views specific to other entity types won't be selectable in this |
|
155 case. Among selectable objects, the implements selector will return a higher |
|
156 score than the second view since it's more specific, so it will be selected as |
|
157 expected. |
|
158 |
|
159 .. _SelectionAPI: |
|
160 |
|
161 API for objects selections |
|
162 ~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
163 |
|
164 Here is the selection API you'll get on every registry. Some of them, as the |
|
165 'etypes' registry, containing entity classes, extend it. In those methods, |
|
166 `*args, **kwargs` is what we call the **context**. Those arguments are given to |
|
167 selectors that will inspect there content and return a score accordingly. |
|
168 |
|
169 .. automethod:: cubicweb.vregistry.Registry.select |
|
170 |
|
171 .. automethod:: cubicweb.vregistry.Registry.select_or_none |
|
172 |
|
173 .. automethod:: cubicweb.vregistry.Registry.possible_objects |
|
174 |
|
175 .. automethod:: cubicweb.vregistry.Registry.object_by_id |
7 """ |
176 """ |
8 __docformat__ = "restructuredtext en" |
177 __docformat__ = "restructuredtext en" |
9 _ = unicode |
178 _ = unicode |
10 |
179 |
11 from logilab.common.decorators import cached, clear_cache |
180 from logilab.common.decorators import cached, clear_cache |