summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2009-10-18 02:27:43 +0000
committerChris McDonough <chrism@agendaless.com>2009-10-18 02:27:43 +0000
commit2cce431f02a37c119eacfc3dfa94af9fe3305de1 (patch)
treef154dbf4937afb3f5d08ed497f6c4cac6f843144
parentf276669b505cb0565dcf854f2e0e751151ccf836 (diff)
downloadpyramid-2cce431f02a37c119eacfc3dfa94af9fe3305de1.tar.gz
pyramid-2cce431f02a37c119eacfc3dfa94af9fe3305de1.tar.bz2
pyramid-2cce431f02a37c119eacfc3dfa94af9fe3305de1.zip
- More than one ``@bfg_view`` decorator may now be stacked on top of
any number of others. Each invocation of the decorator registers a single view. For instance, the following combination of decorators and a function will register two views:: from repoze.bfg.view import bfg_view @bfg_view(name='edit') @bfg_view(name='change') def edit(context, request): pass This makes it possible to associate more than one view configuration for a single callable without requiring ZCML.
-rw-r--r--CHANGES.txt21
-rw-r--r--docs/narr/views.rst21
-rw-r--r--repoze/bfg/tests/grokkedapp/__init__.py16
-rw-r--r--repoze/bfg/tests/grokkedapp/another.py18
-rw-r--r--repoze/bfg/tests/test_integration.py119
-rw-r--r--repoze/bfg/tests/test_view.py21
-rw-r--r--repoze/bfg/tests/test_zcml.py2
-rw-r--r--repoze/bfg/view.py4
-rw-r--r--repoze/bfg/zcml.py7
9 files changed, 159 insertions, 70 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 2b3181d9d..b9de23d9f 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,3 +1,24 @@
+Next release
+============
+
+Features
+--------
+
+- More than one ``@bfg_view`` decorator may now be stacked on top of
+ any number of others. Each invocation of the decorator registers a
+ single view. For instance, the following combination of decorators
+ and a function will register two views::
+
+ from repoze.bfg.view import bfg_view
+
+ @bfg_view(name='edit')
+ @bfg_view(name='change')
+ def edit(context, request):
+ pass
+
+ This makes it possible to associate more than one view configuration
+ for a single callable without requiring ZCML.
+
1.1a6 (2009-10-15)
==================
diff --git a/docs/narr/views.rst b/docs/narr/views.rst
index 3979ec8f6..b52abf57f 100644
--- a/docs/narr/views.rst
+++ b/docs/narr/views.rst
@@ -708,6 +708,27 @@ decorator syntactic sugar), if you wish:
my_view = bfg_view()(MyView)
+More than one ``bfg_view`` decorator can be stacked on top of any
+number of others. Each decorator creates a separate view
+registration. For example:
+
+.. code-block:: python
+ :linenos:
+
+ from repoze.bfg.view import bfg_view
+
+ @bfg_view(name='edit')
+ @bfg_view(name='change')
+ def edit(context, request):
+ pass
+
+This registers the same view under two different names.
+
+.. note:: ``bfg_view`` decorator stacking is a feature new in
+ :mod:`repoze.bfg` 1.1. Previously, these decorators could not be
+ stacked without the effect of the "upper" decorator cancelling the
+ effect of the the decorator beneath it.
+
.. _view_lookup_ordering:
View Lookup Ordering
diff --git a/repoze/bfg/tests/grokkedapp/__init__.py b/repoze/bfg/tests/grokkedapp/__init__.py
index f4a924292..5a00adbeb 100644
--- a/repoze/bfg/tests/grokkedapp/__init__.py
+++ b/repoze/bfg/tests/grokkedapp/__init__.py
@@ -7,6 +7,22 @@ def grokked(context, request):
@bfg_view(request_method='POST')
def grokked_post(context, request):
return 'grokked_post'
+
+@bfg_view(name='stacked2')
+@bfg_view(name='stacked1')
+def stacked(context, request):
+ return 'stacked'
+
+class stacked_class(object):
+ def __init__(self, context, request):
+ self.context = context
+ self.request = request
+
+ def __call__(self):
+ return 'stacked_class'
+
+stacked_class = bfg_view(name='stacked_class1')(stacked_class)
+stacked_class = bfg_view(name='stacked_class2')(stacked_class)
class oldstyle_grokked_class:
def __init__(self, context, request):
diff --git a/repoze/bfg/tests/grokkedapp/another.py b/repoze/bfg/tests/grokkedapp/another.py
index 94be9f74a..7dda1d579 100644
--- a/repoze/bfg/tests/grokkedapp/another.py
+++ b/repoze/bfg/tests/grokkedapp/another.py
@@ -7,7 +7,23 @@ def grokked(context, request):
@bfg_view(request_method='POST', name='another')
def grokked_post(context, request):
return 'another_grokked_post'
-
+
+@bfg_view(name='another_stacked2')
+@bfg_view(name='another_stacked1')
+def stacked(context, request):
+ return 'another_stacked'
+
+class stacked_class(object):
+ def __init__(self, context, request):
+ self.context = context
+ self.request = request
+
+ def __call__(self):
+ return 'another_stacked_class'
+
+stacked_class = bfg_view(name='another_stacked_class1')(stacked_class)
+stacked_class = bfg_view(name='another_stacked_class2')(stacked_class)
+
class oldstyle_grokked_class:
def __init__(self, context, request):
self.context = context
diff --git a/repoze/bfg/tests/test_integration.py b/repoze/bfg/tests/test_integration.py
index 5e386a069..691d64e24 100644
--- a/repoze/bfg/tests/test_integration.py
+++ b/repoze/bfg/tests/test_integration.py
@@ -105,68 +105,43 @@ class TestGrokkedApp(unittest.TestCase):
from repoze.bfg.interfaces import IView
from repoze.bfg.interfaces import IRequest
import repoze.bfg.tests.grokkedapp as package
- actions = zcml_configure('configure.zcml', package)[-10:]
+
+ actions = zcml_configure('configure.zcml', package)
actions.sort()
- action = actions[0]
- self.assertEqual(action[0][1], None)
- self.assertEqual(action[0][2], '')
- self.assertEqual(action[0][3], IRequest)
- self.assertEqual(action[0][4], IView)
-
- action = actions[1]
- self.assertEqual(action[0][1], None)
- self.assertEqual(action[0][2], '')
- self.assertEqual(action[0][3], IRequest)
- self.assertEqual(action[0][4], IView)
-
- action = actions[2]
- self.assertEqual(action[0][1], None)
- self.assertEqual(action[0][2], 'another')
- self.assertEqual(action[0][3], IRequest)
- self.assertEqual(action[0][4], IView)
-
- action = actions[3]
- self.assertEqual(action[0][1], None)
- self.assertEqual(action[0][2], 'another')
- self.assertEqual(action[0][3], IRequest)
- self.assertEqual(action[0][4], IView)
-
- action = actions[4]
- self.assertEqual(action[0][1], None)
- self.assertEqual(action[0][2], 'another_grokked_class')
- self.assertEqual(action[0][3], IRequest)
- self.assertEqual(action[0][4], IView)
-
- action = actions[5]
- self.assertEqual(action[0][1], None)
- self.assertEqual(action[0][2], 'another_grokked_instance')
- self.assertEqual(action[0][3], IRequest)
- self.assertEqual(action[0][4], IView)
-
- action = actions[6]
- self.assertEqual(action[0][1], None)
- self.assertEqual(action[0][2], 'another_oldstyle_grokked_class')
- self.assertEqual(action[0][3], IRequest)
- self.assertEqual(action[0][4], IView)
-
- action = actions[7]
- self.assertEqual(action[0][1], None)
- self.assertEqual(action[0][2], 'grokked_class')
- self.assertEqual(action[0][3], IRequest)
- self.assertEqual(action[0][4], IView)
-
- action = actions[8]
- self.assertEqual(action[0][1], None)
- self.assertEqual(action[0][2], 'grokked_instance')
- self.assertEqual(action[0][3], IRequest)
- self.assertEqual(action[0][4], IView)
-
- action = actions[9]
- self.assertEqual(action[0][1], None)
- self.assertEqual(action[0][2], 'oldstyle_grokked_class')
- self.assertEqual(action[0][3], IRequest)
- self.assertEqual(action[0][4], IView)
+ num = 18
+
+ action_names = [actions[x][0][2] for x in range(len(actions[:num]))]
+ action_types = [(actions[x][0][1],
+ actions[x][0][3],
+ actions[x][0][4]) for x in range(len(actions[:num]))]
+
+ for typ in action_types:
+ self.assertEqual(typ, (None, IRequest, IView))
+
+ self.assertEqual(
+ action_names, [
+ '',
+ '',
+ 'another',
+ 'another',
+ 'another_grokked_class',
+ 'another_grokked_instance',
+ 'another_oldstyle_grokked_class',
+ 'another_stacked1',
+ 'another_stacked2',
+ 'another_stacked_class1',
+ 'another_stacked_class2',
+ 'grokked_class',
+ 'grokked_instance',
+ 'oldstyle_grokked_class',
+ 'stacked1',
+ 'stacked2',
+ 'stacked_class1',
+ 'stacked_class2',
+ ]
+ )
+
ctx = DummyContext()
req = DummyRequest()
@@ -207,6 +182,30 @@ class TestGrokkedApp(unittest.TestCase):
'another_oldstyle_grokked_class')
self.assertEqual(result, 'another_oldstyle_grokked_class')
+ result = render_view_to_response(ctx, req, 'stacked1')
+ self.assertEqual(result, 'stacked')
+
+ result = render_view_to_response(ctx, req, 'stacked2')
+ self.assertEqual(result, 'stacked')
+
+ result = render_view_to_response(ctx, req, 'another_stacked1')
+ self.assertEqual(result, 'another_stacked')
+
+ result = render_view_to_response(ctx, req, 'another_stacked2')
+ self.assertEqual(result, 'another_stacked')
+
+ result = render_view_to_response(ctx, req, 'stacked_class1')
+ self.assertEqual(result, 'stacked_class')
+
+ result = render_view_to_response(ctx, req, 'stacked_class2')
+ self.assertEqual(result, 'stacked_class')
+
+ result = render_view_to_response(ctx, req, 'another_stacked_class1')
+ self.assertEqual(result, 'another_stacked_class')
+
+ result = render_view_to_response(ctx, req, 'another_stacked_class2')
+ self.assertEqual(result, 'another_stacked_class')
+
class DummyContext(object):
pass
diff --git a/repoze/bfg/tests/test_view.py b/repoze/bfg/tests/test_view.py
index eee5a61bd..7b9597194 100644
--- a/repoze/bfg/tests/test_view.py
+++ b/repoze/bfg/tests/test_view.py
@@ -376,7 +376,7 @@ class TestBFGViewDecorator(unittest.TestCase):
""" docstring """
wrapped = decorator(foo)
self.failUnless(wrapped is foo)
- settings = wrapped.__bfg_view_settings__
+ settings = wrapped.__bfg_view_settings__[0]
self.assertEqual(settings['permission'], None)
self.assertEqual(settings['for_'], None)
self.assertEqual(settings['request_type'], None)
@@ -387,7 +387,7 @@ class TestBFGViewDecorator(unittest.TestCase):
""" docstring """
wrapped = decorator(foo)
self.failUnless(wrapped is foo)
- settings = wrapped.__bfg_view_settings__
+ settings = wrapped.__bfg_view_settings__[0]
self.assertEqual(settings['permission'], None)
self.assertEqual(settings['for_'], None)
self.assertEqual(settings['request_type'], None)
@@ -398,11 +398,26 @@ class TestBFGViewDecorator(unittest.TestCase):
""" docstring """
wrapped = decorator(foo)
self.failUnless(wrapped is foo)
- settings = wrapped.__bfg_view_settings__
+ settings = wrapped.__bfg_view_settings__[0]
self.assertEqual(settings['permission'], None)
self.assertEqual(settings['for_'], None)
self.assertEqual(settings['request_type'], None)
+ def test_stacking(self):
+ decorator1 = self._makeOne(name='1')
+ decorator2 = self._makeOne(name='2')
+ def foo():
+ """ docstring """
+ wrapped1 = decorator1(foo)
+ wrapped2 = decorator2(wrapped1)
+ self.failUnless(wrapped1 is foo)
+ self.failUnless(wrapped2 is foo)
+ self.assertEqual(len(wrapped2.__bfg_view_settings__), 2)
+ settings1 = wrapped2.__bfg_view_settings__[0]
+ self.assertEqual(settings1['name'], '1')
+ settings2 = wrapped2.__bfg_view_settings__[1]
+ self.assertEqual(settings2['name'], '2')
+
class TestDefaultForbiddenView(unittest.TestCase):
def _callFUT(self, context, request):
from repoze.bfg.view import default_forbidden_view
diff --git a/repoze/bfg/tests/test_zcml.py b/repoze/bfg/tests/test_zcml.py
index 77137ceba..7538072ca 100644
--- a/repoze/bfg/tests/test_zcml.py
+++ b/repoze/bfg/tests/test_zcml.py
@@ -2390,7 +2390,7 @@ class TestBFGViewGrokker(unittest.TestCase):
containment=None, attr=None, renderer=None,
wrapper_viewname=None, xhr=False, header=None,
accept=None)
- obj.__bfg_view_settings__ = settings
+ obj.__bfg_view_settings__ = [settings]
context = DummyContext()
result = grokker.grok('name', obj, context=context)
self.assertEqual(result, True)
diff --git a/repoze/bfg/view.py b/repoze/bfg/view.py
index d6c47ec0f..7f71f2c57 100644
--- a/repoze/bfg/view.py
+++ b/repoze/bfg/view.py
@@ -394,7 +394,9 @@ class bfg_view(object):
self.header = header
def __call__(self, wrapped):
- wrapped.__bfg_view_settings__ = self.__dict__.copy()
+ settings = getattr(wrapped, '__bfg_view_settings__', [])
+ settings.append(self.__dict__.copy())
+ wrapped.__bfg_view_settings__ = settings
return wrapped
def default_view(context, request, status):
diff --git a/repoze/bfg/zcml.py b/repoze/bfg/zcml.py
index a4c18dda9..681c4da1e 100644
--- a/repoze/bfg/zcml.py
+++ b/repoze/bfg/zcml.py
@@ -693,8 +693,8 @@ class BFGMultiGrokker(martian.core.MultiInstanceOrClassGrokkerBase):
class BFGViewGrokker(martian.InstanceGrokker):
martian.component(BFGViewMarker)
def grok(self, name, obj, **kw):
- if hasattr(obj, '__bfg_view_settings__'):
- settings = obj.__bfg_view_settings__
+ config = getattr(obj, '__bfg_view_settings__', [])
+ for settings in config:
permission = settings['permission']
for_ = settings['for_']
name = settings['name']
@@ -716,8 +716,7 @@ class BFGViewGrokker(martian.InstanceGrokker):
request_param=request_param, containment=containment,
attr=attr, renderer=renderer, wrapper=wrapper,
xhr=xhr, accept=accept, header=header)
- return True
- return False
+ return bool(config)
def exclude(name):
if name.startswith('.'):