Skip to content

Commit

Permalink
mv2
Browse files Browse the repository at this point in the history
git-svn-id: https://svn.bluedynamics.eu/svn/module/bda.bfg.tile/trunk@50 f119a143-da5b-4457-81e3-bee00661a780
  • Loading branch information
jensens committed Nov 12, 2009
1 parent fb6287a commit 01648bb
Show file tree
Hide file tree
Showing 15 changed files with 324 additions and 0 deletions.
Empty file added README.txt
Empty file.
44 changes: 44 additions & 0 deletions setup.py
@@ -0,0 +1,44 @@
from setuptools import setup, find_packages
import sys, os

version = '1.0'
shortdesc = 'Handle web application parts as tiles.'
longdesc = open(os.path.join(os.path.dirname(__file__), 'src', 'bda', 'bfg',
'tile', '_api.txt')).read()

setup(name='bda.bfg.tile',
version=version,
description=shortdesc,
long_description=longdesc,
classifiers=[
'Development Status :: 3 - Alpha',
'Environment :: Web Environment',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
],
keywords='',
author='BlueDynamics Alliance',
author_email='dev@bluedynamics.com',
url=u'https://svn.bluedynamics.net/svn/internal/bda.tile',
license='GNU General Public Licence',
packages=find_packages('src'),
package_dir = {'': 'src'},
namespace_packages=['bda', 'bda.bfg'],
include_package_data=True,
zip_safe=False,
install_requires=[
'setuptools',
'repoze.bfg',
],
dependency_links = [
"http://dist.repoze.org/bfg/1.0/",
],
extras_require = dict(
test=[
'interlude',
]
),
tests_require=['interlude'],
test_suite="bda.bfg.tile.tests.test_suite",
)
1 change: 1 addition & 0 deletions src/bda/__init__.py
@@ -0,0 +1 @@
__import__('pkg_resources').declare_namespace(__name__)
1 change: 1 addition & 0 deletions src/bda/bfg/__init__.py
@@ -0,0 +1 @@
__import__('pkg_resources').declare_namespace(__name__)
7 changes: 7 additions & 0 deletions src/bda/bfg/tile/__init__.py
@@ -0,0 +1,7 @@
from _api import render_template
from _api import render_template_to_response
from _api import ITile
from _api import TileRenderer
from _api import Tile
from _api import registerTile
from _api import tile
176 changes: 176 additions & 0 deletions src/bda/bfg/tile/_api.py
@@ -0,0 +1,176 @@
import os
from zope.interface import Interface, Attribute, implements
from zope.component import queryUtility
from zope.component import getMultiAdapter
from zope.component import ComponentLookupError
from repoze.bfg.threadlocal import get_current_registry
from webob import Response
from webob.exc import HTTPFound
from repoze.bfg.interfaces import IRequest
from repoze.bfg.interfaces import IResponseFactory
from repoze.bfg.interfaces import IAuthenticationPolicy
from repoze.bfg.interfaces import IViewPermission
from repoze.bfg.path import caller_package
from repoze.bfg.renderers import template_renderer_factory
from repoze.bfg.chameleon_zpt import ZPTTemplateRenderer
#from repoze.bfg.security import ViewPermissionFactory
from repoze.bfg.security import has_permission

class ITile(Interface):
"""returns on call some HTML snippet."""

def __call__(model, request):
"""Renders the tile.
It's intended to work this way: First it calls its own prepare method,
then it checks its own show attribute. If this returns True it renders
the template in the context of the ITile implementing class instance.
"""

def prepare():
"""Prepares the tile.
I.e. fetch data to display ...
"""

show = Attribute("""Render this tile?""")

def _update_kw(**kw):
if not ('request' in kw and 'model' in kw):
raise ValueError, "Eexpected kwargs missing: model, request."
kw.update({'tile': TileRenderer(kw['model'], kw['request'])})
return kw

def _redirect(kw):
if kw['request'].environ.get('redirect'):
return True
return False

def render_template(path, **kw):
kw = _update_kw(**kw)
if _redirect(kw):
return u''
renderer = template_renderer_factory(path, ZPTTemplateRenderer)
return renderer(kw, {})

def render_template_to_response(path, **kw):
kw = _update_kw(**kw)
kw['request'].environ['redirect'] = None
renderer = template_renderer_factory(path, ZPTTemplateRenderer)
result = renderer(kw, {})
if _redirect(kw):
return HTTPFound(location=kw['request'].environ['redirect'])
response_factory = queryUtility(IResponseFactory, default=Response)
return response_factory(result)

class Tile(object):
implements(ITile)

def __init__(self, path, attribute):
self.path = path
self.attribute = attribute

def __call__(self, model, request):
self.model = model
self.request = request
self.prepare() # XXX maybe remove.
if not self.show:
return u''
if self.path:
try:
# XXX: do not catch exception.
return render_template(self.path, request=request,
model=model, context=self)
except Exception, e:
return u"Error:<br /><pre>%s</pre>" % e
renderer = getattr(self, self.attribute)
result = renderer()
return result

@property
def show(self):
return True

def prepare(self):
pass

def render(self):
return u''

def redirect(self, url):
self.request.environ['redirect'] = url

@property
def nodeurl(self):
relpath = [p for p in self.model.path if p is not None]
return '/'.join([self.request.application_url] + relpath)

class TileRenderer(object):

def __init__(self, model, request):
self.model, self.request = model, request

def __call__(self, name):
registry = get_current_registry()
# XXX fix me. new API in repoze.bfg
#secured = not not registry.queryUtility(IAuthenticationPolicy)
#if secured:
# permitted = registry.getMultiAdapter((self.model, self.request),
# IViewPermission,
# name=name)
# if not permitted:
# return u'permission denied'
try:
tile = getMultiAdapter((self.model, self.request), ITile, name=name)
except ComponentLookupError, e:
return u"Tile with name '%s' not found:<br /><pre>%s</pre>" % \
(name, e)
return tile

# Registration
def registerTile(name, path=None, attribute='render',
interface=Interface, _class=Tile, permission='view'):
if isinstance(interface, basestring):
pass # XXX: lookup
if path:
if not (':' in path or os.path.isabs(path)):
caller = caller_package(level=1)
path = '%s:%s' % (caller.__name__, path)
factory = _class(path, attribute)
registry = get_current_registry()
registry.registerAdapter(factory, [interface, IRequest],
ITile, name, event=False)
# XXX fix me. new API in repoze.bfg
#if permission:
# factory = ViewPermissionFactory(permission)
# registry.registerAdapter(factory, [interface, IRequest],
# IViewPermission, name)

class tile(object):
"""Tile decorator.
"""

def __init__(self, name, path=None, attribute='render',
interface=Interface, permission='view', level=2):
"""name to register as, path to template, interface adapting to.
level is a special to make doctests pass the magic path-detection.
you should never need latter.
"""
self.name = name
self.path = path
if path:
if not (':' in path or os.path.isabs(path)):
caller = caller_package(level)
self.path = '%s:%s' % (caller.__name__, path)
self.attribute = attribute
self.interface = interface
self.permission = permission

def __call__(self, ob):
registerTile(self.name,
self.path,
self.attribute,
interface=self.interface,
_class=ob,
permission=self.permission)
return ob
63 changes: 63 additions & 0 deletions src/bda/bfg/tile/_api.txt
@@ -0,0 +1,63 @@
A tile is a piece of web application, i.e. a form, a navigation, etc.

Splitting your application in such small and logic application parts makes it
easy to re-use this application, simplifies application ajaxification and
the use of same application parts in different manners.

Imports.:

>>> from bda.bfg.tile import Tile
>>> from bda.bfg.tile import TileRenderer
>>> from bda.bfg.tile import registerTile
>>> from bda.bfg.tile import tile

We need some dummies as model and request.:

>>> class Model(object): pass
>>> model = Model()
>>> from repoze.bfg.request import DEFAULT_REQUEST_FACTORIES
>>> request = DEFAULT_REQUEST_FACTORIES[None]['factory'](environ={})

The pure Tile itself. Normally you do not create this directly, this is done
due registration, see below.:

>>> mytile = Tile('testdata/tile1.pt', None)
>>> mytile(model, request)
u'<span>Tile One</span>'

Register a tile using the prior template testtemplate. When no object is given,
the default tile is instanciated as above.:

>>> registerTile('tileone', 'testdata/tile1.pt')

Render the already registered tile.:

>>> TileRenderer(model, request)('tileone')
u'<span>Tile One</span>'

Now the decorator - level=1 is needed for the doctest only to reduce the module
level.:

>>> @tile('tiletwo', 'testdata/tile2.pt', level=1)
... class Tile2(Tile):
... data = u'custom'
>>> TileRenderer(model, request)('tiletwo')
u'<span>Tile Two: <b><span>Tile One</span></b></span>'

You can define an attribute which is responsible to render the tile instead of
defining a template. By default ``render`` is taken. With the keyword argument
``attribute`` you can point to a different attribute.:

>>> @tile('attrtile')
... class Tile2(Tile):
... def render(self):
... return u'<h1>Rendered via attribute call</h1>'
>>> TileRenderer(model, request)('attrtile')
u'<h1>Rendered via attribute call</h1>'

>>> @tile('attrtile', attribute='foobar')
... class Tile2(Tile):
... def foobar(self):
... return u'<h1>Rendered via attribute foobar call</h1>'
>>> TileRenderer(model, request)('attrtile')
u'<h1>Rendered via attribute foobar call</h1>'
1 change: 1 addition & 0 deletions src/bda/bfg/tile/testdata/main1.pt
@@ -0,0 +1 @@
<html><body><div tal:content="structure tile('tileone')" /></body></html>
1 change: 1 addition & 0 deletions src/bda/bfg/tile/testdata/main2.pt
@@ -0,0 +1 @@
<html><body><div tal:content="structure tile('tiletwo')" /></body></html>
1 change: 1 addition & 0 deletions src/bda/bfg/tile/testdata/main3.pt
@@ -0,0 +1 @@
<html><body><div tal:content="structure tile('tilethree')" /></body></html>
1 change: 1 addition & 0 deletions src/bda/bfg/tile/testdata/tile1.pt
@@ -0,0 +1 @@
<span>Tile One</span>
1 change: 1 addition & 0 deletions src/bda/bfg/tile/testdata/tile2.pt
@@ -0,0 +1 @@
<span>Tile Two: <b tal:content="structure tile('tileone')"/></span>
1 change: 1 addition & 0 deletions src/bda/bfg/tile/testdata/tile2_1.pt
@@ -0,0 +1 @@
<span>second level</span>
1 change: 1 addition & 0 deletions src/bda/bfg/tile/testdata/tile3.pt
@@ -0,0 +1 @@
<span>Tile Three: <tal: tal:replace="context.data" /></span>
25 changes: 25 additions & 0 deletions src/bda/bfg/tile/tests.py
@@ -0,0 +1,25 @@
import unittest
import doctest
from pprint import pprint
from interlude import interact

optionflags = doctest.NORMALIZE_WHITESPACE | \
doctest.ELLIPSIS #| \
#doctest.REPORT_ONLY_FIRST_FAILURE

TESTFILES = [
'_api.txt',
]

def test_suite():
return unittest.TestSuite([
doctest.DocFileSuite(
file,
optionflags=optionflags,
globs={'interact': interact,
'pprint': pprint},
) for file in TESTFILES
])

if __name__ == '__main__':
unittest.main(defaultTest='test_suite')

0 comments on commit 01648bb

Please sign in to comment.