1
2 u"""
3 :Copyright:
4
5 Copyright 2006 - 2014
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 __author__ = u"Andr\xe9 Malo"
29 __docformat__ = "restructuredtext en"
30
31 from tdi import ModelMissingError
32 from tdi import interfaces as _interfaces
36 """
37 Regular Render-Adapter implementation
38
39 :See: `ModelAdapterInterface`
40 """
41 __implements__ = [_interfaces.ModelAdapterInterface]
42
43 - def __new__(cls, model, requiremethods=False, requirescopes=False):
44 """
45 Construct
46
47 :Parameters:
48 `model` : any
49 User model
50
51 `requiremethods` : ``bool``
52 Require methods to exist?
53
54 `requirescopes` : ``bool``
55 Require scopes to exist?
56
57 :Return: Render adapter
58 :Rtype: `ModelAdapterInterface`
59 """
60
61
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 pass
72 unset = unset()
73
74 def new(model):
75 """ Create adapter for a new model """
76 return cls(model,
77 requiremethods=requiremethods,
78 requirescopes=requirescopes,
79 )
80
81 def modelmethod(prefix, name, scope, noauto):
82 """
83 Build the method name from prefix and node name and resolve
84
85 This implements the default look up.
86
87 :Parameters:
88 `prefix` : ``str``
89 The method prefix (``render``, or ``separate``)
90
91 `name` : ``str``
92 The node name
93
94 `scope` : ``str``
95 Scope
96
97 `noauto` : ``bool``
98 No automatic method calling?
99
100 :Return: The method or ``None``
101 :Rtype: ``callable``
102
103 :Exceptions:
104 - `ModelMissingError` : The method was not found, but all
105 methods are required
106 """
107 if name is None or noauto:
108 return None
109 if scope in models:
110 model = models[scope]
111 else:
112 model = models['']
113 scope_part = None
114 for part in scope.split('.'):
115 if not scope_part:
116 scope_part = part
117 else:
118 scope_part = '%s.%s' % (scope_part, part)
119 if scope_part in models:
120 model = models[scope_part]
121 else:
122 model = getattr_(model, 'scope_' + part, unset)
123 if model is unset:
124 if requirescopes:
125 raise ModelMissingError(scope_part)
126 model = None
127 models[scope_part] = model
128
129 method = getattr_(model, "%s_%s" % (prefix, name), unset)
130 if method is unset:
131 if requiremethods:
132 raise ModelMissingError("%s_%s" % (prefix, name))
133 method = None
134 return method
135
136 self.modelmethod = modelmethod
137 self.new = new
138 self.emit_escaped = False
139
140 return self
141
142 @classmethod
144 """
145 Create prerender adapter from model
146
147 :Parameters:
148 `model` : any
149 User model
150
151 `attr` : ``dict``
152 Attribute name mapping. The keys 'scope' and 'tdi' are recognized.
153 If omitted or ``None``, the default attribute names are applied
154 ('tdi:scope' and 'tdi').
155
156 :Return: Prerender adapter
157 :Rtype: `ModelAdapterInterface`
158 """
159 return PreRenderWrapper(cls(model), attr=attr)
160
163 """
164 Pre-render wrapper adapter
165
166 :See: `ModelAdapterInterface`
167 """
168 __implements__ = [_interfaces.ModelAdapterInterface]
169
170 - def __new__(cls, adapter, attr=None):
171 """
172 Construct
173
174 :Parameters:
175 `adapter` : `ModelAdapterInterface`
176 model adapter for resolving methods
177
178 `attr` : ``dict``
179 Attribute name mapping. The keys 'scope' and 'tdi' are recognized.
180 If omitted or ``None``, the default attribute names are applied
181 ('tdi:scope' and 'tdi').
182
183 :Return: Render adapter
184 :Rtype: `ModelAdapterInterface`
185 """
186
187 self = object.__new__(cls)
188
189 scope_attr = 'tdi:scope'
190 tdi_attr = 'tdi'
191 if attr is not None:
192 scope_attr = attr.get('scope', scope_attr)
193 tdi_attr = attr.get('tdi', tdi_attr)
194 attr = dict(tdi=tdi_attr, scope=scope_attr)
195
196 def new(model):
197 """ Create adapter for a new model """
198 return cls(adapter.new(model), attr=attr)
199
200 def modelmethod(prefix, name, scope, noauto):
201 """
202 Build the method name from prefix and node name and resolve
203
204 This asks the passed adapter and if the particular method is not
205 found it generates its own, which restores the tdi attributes
206 (but not tdi:overlay).
207
208 :Parameters:
209 `prefix` : ``str``
210 The method prefix (``render``, or ``separate``)
211
212 `name` : ``str``
213 The node name
214
215 `scope` : ``str``
216 Scope
217
218 `noauto` : ``bool``
219 No automatic method calling?
220
221 :Return: The method or ``None``
222 :Rtype: ``callable``
223 """
224 try:
225 method = adapter.modelmethod(prefix, name, scope, noauto)
226 except ModelMissingError:
227 pass
228 else:
229 if method is not None:
230 return method
231
232
233
234
235
236 if prefix == 'separate':
237 return None
238
239
240
241
242
243
244
245 def repeat(node, item, ctx):
246 """ Repeater """
247 if item:
248 return node.remove()
249 node.ctx = ctx
250
251 def setscope(node, scope=scope):
252 """ Special attribute helper """
253 node[scope_attr] = (
254 '=' + (node.hiddenelement and '-' or '+') + scope
255 )
256
257 def render(node, name=name, sep=False):
258 """ Generated render method """
259 try:
260 toremove = node['tdi:prerender'] == 'remove-node'
261 del node['tdi:prerender']
262 except KeyError:
263 toremove = False
264
265 setscope(node)
266 if not toremove:
267 if name is not None:
268 flags = node.hiddenelement and '-' or '+'
269 if noauto:
270 flags += '*'
271 if sep:
272 flags += ':'
273 node[tdi_attr] = flags + name
274 node.hiddenelement = False
275
276 def separate(node, ctx):
277 """ Separator """
278 node.ctx = ctx
279 return render(node, sep=True)
280
281 node.repeat(repeat, (0, 1), node.ctx, separate=separate)
282
283 if name is None:
284 return setscope
285 return render
286
287 self.modelmethod = modelmethod
288 self.new = new
289 self.emit_escaped = True
290
291 return self
292
293
294 from tdi import c
295 c = c.load('impl')
296 if c is not None:
297 RenderAdapter = c.RenderAdapter
298 PreRenderWrapper = c.PreRenderWrapper
299 del c
300