Package tdi :: Module model_adapters
[frames] | no frames]

Source Code for Module tdi.model_adapters

  1  # -*- coding: ascii -*- 
  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 
33 34 35 -class RenderAdapter(object):
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 # pylint: disable = R0912 61 # (too many branches) 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): # pylint: disable = C0103, C0111 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
143 - def for_prerender(cls, model, attr=None):
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
161 162 -class PreRenderWrapper(object):
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 # pylint: disable = R0912 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 # These methods we only see of the model repeats a node, but 233 # doesn't define a separator logic. We do not want to write out 234 # the special node stuff in this case (since the separators would 235 # be alone after that). 236 if prefix == 'separate': 237 return None 238 239 # The node is repeated in order to get our hands on 240 # a possible separator. The second iteration of the node is simply 241 # removed, so we keep the node itself and its separator. 242 243 # However, by repeating the node we override an existing context 244 # of the node. So we pass it explicitly and override it again. 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