1
2 r"""
3 :Copyright:
4
5 Copyright 2006 - 2015
6 Andr\xe9 Malo or his licensors, as applicable
7
8 :License:
9
10 Licensed under the Apache License, Version 2.0 (the "License");
11 you may not use this file except in compliance with the License.
12 You may obtain a copy of the License at
13
14 http://www.apache.org/licenses/LICENSE-2.0
15
16 Unless required by applicable law or agreed to in writing, software
17 distributed under the License is distributed on an "AS IS" BASIS,
18 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 See the License for the specific language governing permissions and
20 limitations under the License.
21
22 ================
23 Model Adapters
24 ================
25
26 Model adapter implementations.
27 """
28 if __doc__:
29
30 __doc__ = __doc__.encode('ascii').decode('unicode_escape')
31 __author__ = r"Andr\xe9 Malo".encode('ascii').decode('unicode_escape')
32 __docformat__ = "restructuredtext en"
33
34 from ._exceptions import ModelMissingError
35 from . import interfaces as _interfaces
39 """
40 Regular Render-Adapter implementation
41
42 :See: `ModelAdapterInterface`
43 """
44 __implements__ = [_interfaces.ModelAdapterInterface]
45
46 - def __new__(cls, model, requiremethods=False, requirescopes=False):
47 """
48 Construct
49
50 :Parameters:
51 `model` : any
52 User model
53
54 `requiremethods` : ``bool``
55 Require methods to exist?
56
57 `requirescopes` : ``bool``
58 Require scopes to exist?
59
60 :Return: Render adapter
61 :Rtype: `ModelAdapterInterface`
62 """
63 self = object.__new__(cls)
64
65 requiremethods = bool(requiremethods)
66 requirescopes = bool(requirescopes)
67 getattr_ = getattr
68 models = {'': model}
69
70 class unset(object):
71
72 pass
73 unset = unset()
74
75 def new(model):
76 """ Create adapter for a new model """
77 return cls(
78 model,
79 requiremethods=requiremethods,
80 requirescopes=requirescopes,
81 )
82
83 def modelmethod(prefix, name, scope, noauto):
84 """
85 Build the method name from prefix and node name and resolve
86
87 This implements the default look up.
88
89 :Parameters:
90 `prefix` : ``str``
91 The method prefix (``render``, or ``separate``)
92
93 `name` : ``str``
94 The node name
95
96 `scope` : ``str``
97 Scope
98
99 `noauto` : ``bool``
100 No automatic method calling?
101
102 :Return: The method or ``None``
103 :Rtype: ``callable``
104
105 :Exceptions:
106 - `ModelMissingError` : The method was not found, but all
107 methods are required
108 """
109 if name is None or noauto:
110 return None
111 if scope in models:
112 model = models[scope]
113 else:
114 model = models['']
115 scope_part = None
116 for part in scope.split('.'):
117 if not scope_part:
118 scope_part = part
119 else:
120 scope_part = '%s.%s' % (scope_part, part)
121 if scope_part in models:
122 model = models[scope_part]
123 else:
124 model = getattr_(model, 'scope_' + part, unset)
125 if model is unset:
126 if requirescopes:
127 raise ModelMissingError(scope_part)
128 model = None
129 models[scope_part] = model
130
131 method = getattr_(model, "%s_%s" % (prefix, name), unset)
132 if method is unset:
133 if requiremethods:
134 raise ModelMissingError("%s_%s" % (prefix, name))
135 method = None
136 return method
137
138 self.modelmethod = modelmethod
139 self.new = new
140 self.emit_escaped = False
141
142 return self
143
144 @classmethod
146 """
147 Create prerender adapter from model
148
149 :Parameters:
150 `model` : any
151 User model
152
153 `attr` : ``dict``
154 Attribute name mapping. The keys 'scope' and 'tdi' are recognized.
155 If omitted or ``None``, the default attribute names are applied
156 ('tdi:scope' and 'tdi').
157
158 :Return: Prerender adapter
159 :Rtype: `ModelAdapterInterface`
160 """
161 return PreRenderWrapper(cls(model), attr=attr)
162
165 """
166 Pre-render wrapper adapter
167
168 :See: `ModelAdapterInterface`
169 """
170 __implements__ = [_interfaces.ModelAdapterInterface]
171
172 - def __new__(cls, adapter, attr=None):
173 """
174 Construct
175
176 :Parameters:
177 `adapter` : `ModelAdapterInterface`
178 model adapter for resolving methods
179
180 `attr` : ``dict``
181 Attribute name mapping. The keys 'scope' and 'tdi' are recognized.
182 If omitted or ``None``, the default attribute names are applied
183 ('tdi:scope' and 'tdi').
184
185 :Return: Render adapter
186 :Rtype: `ModelAdapterInterface`
187 """
188 self = object.__new__(cls)
189
190 scope_attr = 'tdi:scope'
191 tdi_attr = 'tdi'
192 if attr is not None:
193 scope_attr = attr.get('scope', scope_attr)
194 tdi_attr = attr.get('tdi', tdi_attr)
195 attr = dict(tdi=tdi_attr, scope=scope_attr)
196
197 def new(model):
198 """ Create adapter for a new model """
199 return cls(adapter.new(model), attr=attr)
200
201 def modelmethod(prefix, name, scope, noauto):
202 """
203 Build the method name from prefix and node name and resolve
204
205 This asks the passed adapter and if the particular method is not
206 found it generates its own, which restores the tdi attributes
207 (but not tdi:overlay).
208
209 :Parameters:
210 `prefix` : ``str``
211 The method prefix (``render``, or ``separate``)
212
213 `name` : ``str``
214 The node name
215
216 `scope` : ``str``
217 Scope
218
219 `noauto` : ``bool``
220 No automatic method calling?
221
222 :Return: The method or ``None``
223 :Rtype: ``callable``
224 """
225 try:
226 method = adapter.modelmethod(prefix, name, scope, noauto)
227 except ModelMissingError:
228 pass
229 else:
230 if method is not None:
231 return method
232
233
234
235
236
237 if prefix == 'separate':
238 return None
239
240
241
242
243
244
245
246 def repeat(node, item, ctx):
247 """ Repeater """
248 if item:
249 return node.remove()
250 node.ctx = ctx
251
252 def setscope(node, scope=scope):
253 """ Special attribute helper """
254 node[scope_attr] = (
255 '=' + (node.hiddenelement and '-' or '+') + scope
256 )
257
258 def render(node, name=name, sep=False):
259 """ Generated render method """
260 try:
261 toremove = node['tdi:prerender'] == 'remove-node'
262 del node['tdi:prerender']
263 except KeyError:
264 toremove = False
265
266 setscope(node)
267 if not toremove:
268 if name is not None:
269 flags = node.hiddenelement and '-' or '+'
270 if noauto:
271 flags += '*'
272 if sep:
273 flags += ':'
274 node[tdi_attr] = flags + name
275 node.hiddenelement = False
276
277 def separate(node, ctx):
278 """ Separator """
279 node.ctx = ctx
280 return render(node, sep=True)
281
282 node.repeat(repeat, (0, 1), node.ctx, separate=separate)
283
284 if name is None:
285 return setscope
286 return render
287
288 self.modelmethod = modelmethod
289 self.new = new
290 self.emit_escaped = True
291
292 return self
293
294
295 from . import c
296 c = c.load('impl')
297 if c is not None:
298 RenderAdapter = c.RenderAdapter
299 PreRenderWrapper = c.PreRenderWrapper
300 del c
301