27 |
27 |
28 import sys |
28 import sys |
29 from os import listdir, stat |
29 from os import listdir, stat |
30 from os.path import dirname, join, realpath, split, isdir |
30 from os.path import dirname, join, realpath, split, isdir |
31 from logging import getLogger |
31 from logging import getLogger |
|
32 import types |
32 |
33 |
33 from cubicweb import CW_SOFTWARE_ROOT, set_log_methods |
34 from cubicweb import CW_SOFTWARE_ROOT, set_log_methods |
34 from cubicweb import RegistryNotFound, ObjectNotFound, NoSelectableObject |
35 from cubicweb import RegistryNotFound, ObjectNotFound, NoSelectableObject |
35 |
36 |
36 |
37 |
86 rset may be None. If not, row and col arguments may be optionally |
87 rset may be None. If not, row and col arguments may be optionally |
87 given if the registry is scoring a given row or a given cell of |
88 given if the registry is scoring a given row or a given cell of |
88 the result set (both row and col are int if provided). |
89 the result set (both row and col are int if provided). |
89 """ |
90 """ |
90 raise NotImplementedError(cls) |
91 raise NotImplementedError(cls) |
91 |
|
92 |
|
93 class autoselectors(type): |
|
94 """implements __selectors__ / __select__ compatibility layer so that: |
|
95 |
|
96 __select__ = chainall(classmethod(A, B, C)) |
|
97 |
|
98 can be replaced by something like: |
|
99 |
|
100 __selectors__ = (A, B, C) |
|
101 """ |
|
102 def __new__(mcs, name, bases, classdict): |
|
103 if '__select__' in classdict and '__selectors__' in classdict: |
|
104 raise TypeError("__select__ and __selectors__ " |
|
105 "can't be used together") |
|
106 if '__select__' not in classdict and '__selectors__' in classdict: |
|
107 selectors = classdict['__selectors__'] |
|
108 if not isinstance(selectors, (tuple, list)): |
|
109 selectors = (selectors,) |
|
110 if len(selectors) > 1: |
|
111 classdict['__select__'] = classmethod(chainall(*selectors)) |
|
112 else: |
|
113 classdict['__select__'] = classmethod(selectors[0]) |
|
114 return super(autoselectors, mcs).__new__(mcs, name, bases, classdict) |
|
115 |
|
116 def __setattr__(self, attr, value): |
|
117 if attr == '__selectors__': |
|
118 self.__select__ = classmethod(chainall(*value)) |
|
119 super(autoselectors, self).__setattr__(attr, value) |
|
120 |
92 |
121 |
93 |
122 class VObject(object): |
94 class VObject(object): |
123 """visual object, use to be handled somehow by the visual components |
95 """visual object, use to be handled somehow by the visual components |
124 registry. |
96 registry. |
140 (__select__ and __selectors__ are mutually exclusive) |
112 (__select__ and __selectors__ are mutually exclusive) |
141 |
113 |
142 Moreover, the `__abstract__` attribute may be set to True to indicate |
114 Moreover, the `__abstract__` attribute may be set to True to indicate |
143 that a vobject is abstract and should not be registered |
115 that a vobject is abstract and should not be registered |
144 """ |
116 """ |
145 __metaclass__ = autoselectors |
|
146 # necessary attributes to interact with the registry |
117 # necessary attributes to interact with the registry |
147 id = None |
118 id = None |
148 __registry__ = None |
119 __registry__ = None |
149 __registerer__ = None |
120 __registerer__ = None |
150 __select__ = None |
121 __select__ = None |
155 |
126 |
156 It must return the object that will be actually registered (this |
127 It must return the object that will be actually registered (this |
157 may be the right hook to create an instance for example). By |
128 may be the right hook to create an instance for example). By |
158 default the vobject is returned without any transformation. |
129 default the vobject is returned without any transformation. |
159 """ |
130 """ |
|
131 cls.__select__ = cls.build___select__() |
160 return cls |
132 return cls |
161 |
133 |
162 @classmethod |
134 @classmethod |
163 def selected(cls, *args, **kwargs): |
135 def selected(cls, *args, **kwargs): |
164 """called by the registry when the vobject has been selected. |
136 """called by the registry when the vobject has been selected. |
172 |
144 |
173 @classmethod |
145 @classmethod |
174 def classid(cls): |
146 def classid(cls): |
175 """returns a unique identifier for the vobject""" |
147 """returns a unique identifier for the vobject""" |
176 return '%s.%s' % (cls.__module__, cls.__name__) |
148 return '%s.%s' % (cls.__module__, cls.__name__) |
|
149 |
|
150 @classmethod |
|
151 def build___select__(cls): |
|
152 classdict = cls.__dict__ |
|
153 if '__select__' in classdict and '__selectors__' in classdict: |
|
154 raise TypeError("__select__ and __selectors__ can't be used together") |
|
155 if '__selectors__' in classdict: |
|
156 # case where __selectors__ is defined locally (but __select__ |
|
157 # is in a parent class) |
|
158 selectors = classdict['__selectors__'] |
|
159 if len(selectors) == 1: |
|
160 # micro optimization: don't bother with AndSelector if there's |
|
161 # only one selector |
|
162 return _instantiate_selector(selectors[0]) |
|
163 return AndSelector(_instantiate_selector(selector) |
|
164 for selector in selectors) |
|
165 return cls.__select__ |
177 |
166 |
178 |
167 |
179 class VRegistry(object): |
168 class VRegistry(object): |
180 """class responsible to register, propose and select the various |
169 """class responsible to register, propose and select the various |
181 elements used to build the web interface. Currently, we have templates, |
170 elements used to build the web interface. Currently, we have templates, |
663 |
652 |
664 """ |
653 """ |
665 return type(selector_func.__name__, (Selector,), |
654 return type(selector_func.__name__, (Selector,), |
666 {'__call__': lambda self, *args: selector_func(*args)}) |
655 {'__call__': lambda self, *args: selector_func(*args)}) |
667 |
656 |
|
657 def _instantiate_selector(selector): |
|
658 """ensures `selector` is a `Selector` instance |
|
659 |
|
660 NOTE: This should only be used locally in build___select__() |
|
661 """ |
|
662 if isinstance(selector, types.FunctionType): |
|
663 return objectify_selector(selector)() |
|
664 if issubclass(selector, Selector): |
|
665 return selector() |
|
666 return selector |
|
667 |
668 |
668 |
669 class AndSelector(MultiSelector): |
669 class AndSelector(MultiSelector): |
670 """and-chained selectors (formerly known as chainall)""" |
670 """and-chained selectors (formerly known as chainall)""" |
671 def __call__(self, cls, *args, **kwargs): |
671 def __call__(self, cls, *args, **kwargs): |
672 score = 0 |
672 score = 0 |