From eecdbc34962b00e35d41039af014462cf558acee Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Sun, 3 Jan 2010 03:39:30 +0000 Subject: Features -------- - The ``Configurator.add_view`` method now accepts an argument named ``context``. This is an alias for the older argument named ``for_``; it is preferred over ``for_``, but ``for_`` will continue to be supported "forever". - The ``view`` ZCML directive now accepts an attribute named ``context``. This is an alias for the older attribute named ``for``; it is preferred over ``for``, but ``for`` will continue to be supported "forever". - The ``Configurator.add_route`` method now accepts an argument named ``view_context``. This is an alias for the older argument named ``view_for``; it is preferred over ``view_for``, but ``view_for`` will continue to be supported "forever". - The ``route`` ZCML directive now accepts an attribute named ``view_context``. This is an alias for the older attribute named ``view_for``; it is preferred over ``view_for``, but ``view_for`` will continue to be supported "forever". Documentation and Paster Templates ---------------------------------- - All uses of the ``Configurator.add_view`` method that used its ``for_`` argument now use the ``context``argument instead. - All uses of the ``Configurator.add_route`` method that used its ``view_for`` argument now use the ``view_context``argument instead. - All uses of the ``view`` ZCML directive that used its ``for`` attribute now use the ``context`` attribute instead. - All uses of the ``route`` ZCML directive that used its ``view_for`` attribute now use the ``view_context`` attribute instead. --- CHANGES.txt | 38 +++++++++ TODO.txt | 3 - docs/designdefense.rst | 6 +- docs/glossary.rst | 16 ++-- docs/narr/MyProject/myproject/configure.zcml | 2 +- docs/narr/extending.rst | 4 +- docs/narr/hooks.rst | 10 +-- docs/narr/hybrid.rst | 13 ++-- docs/narr/project.rst | 9 ++- docs/narr/security.rst | 4 +- docs/narr/views.rst | 90 +++++++++++----------- docs/tutorials/bfgwiki/basiclayout.rst | 13 ++-- .../bfgwiki/src/authorization/tutorial/login.py | 4 +- .../bfgwiki/src/authorization/tutorial/views.py | 8 +- .../src/basiclayout/tutorial/configure.zcml | 2 +- .../bfgwiki/src/models/tutorial/configure.zcml | 2 +- .../bfgwiki/src/viewdecorators/tutorial/views.py | 8 +- .../bfgwiki/src/views/tutorial/configure.zcml | 8 +- docs/tutorials/bfgwiki/viewdecorators.rst | 41 +++++----- docs/zcml.rst | 15 ++-- repoze/bfg/configuration.py | 45 +++++++---- .../alchemy/+package+/configure.zcml | 4 +- .../starter/+package+/configure.zcml | 2 +- .../paster_templates/zodb/+package+/configure.zcml | 2 +- repoze/bfg/tests/test_configuration.py | 43 ++++++++++- repoze/bfg/tests/test_view.py | 10 +-- repoze/bfg/tests/test_zcml.py | 78 +++++++++++++++++++ repoze/bfg/view.py | 15 ++-- repoze/bfg/zcml.py | 30 +++++--- 29 files changed, 356 insertions(+), 169 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index f1bf4e7f9..b24133989 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,6 +1,44 @@ Next release ============ +Features +-------- + +- The ``Configurator.add_view`` method now accepts an argument named + ``context``. This is an alias for the older argument named + ``for_``; it is preferred over ``for_``, but ``for_`` will continue + to be supported "forever". + +- The ``view`` ZCML directive now accepts an attribute named + ``context``. This is an alias for the older attribute named + ``for``; it is preferred over ``for``, but ``for`` will continue to + be supported "forever". + +- The ``Configurator.add_route`` method now accepts an argument named + ``view_context``. This is an alias for the older argument named + ``view_for``; it is preferred over ``view_for``, but ``view_for`` + will continue to be supported "forever". + +- The ``route`` ZCML directive now accepts an attribute named + ``view_context``. This is an alias for the older attribute named + ``view_for``; it is preferred over ``view_for``, but ``view_for`` + will continue to be supported "forever". + +Documentation and Paster Templates +---------------------------------- + +- All uses of the ``Configurator.add_view`` method that used its + ``for_`` argument now use the ``context``argument instead. + +- All uses of the ``Configurator.add_route`` method that used its + ``view_for`` argument now use the ``view_context``argument instead. + +- All uses of the ``view`` ZCML directive that used its ``for`` + attribute now use the ``context`` attribute instead. + +- All uses of the ``route`` ZCML directive that used its ``view_for`` + attribute now use the ``view_context`` attribute instead. + Documentation Licensing ----------------------- diff --git a/TODO.txt b/TODO.txt index 3a49b8143..14063c92d 100644 --- a/TODO.txt +++ b/TODO.txt @@ -7,6 +7,3 @@ - Get rid of ITraverser hook in favor of IViewLookup. -- Add "context" as an alias for "for" in ZCML and imperative config - bits. - diff --git a/docs/designdefense.rst b/docs/designdefense.rst index e4ce4f56d..296589c62 100644 --- a/docs/designdefense.rst +++ b/docs/designdefense.rst @@ -655,9 +655,9 @@ administrative interface), you can register a route like ```` and then associate "management" views in your code by using the ``route_name`` argument to a ``view`` configuration, e.g. ````. If you wire things up this -way someone then walks up to for example, ``/manage/ob1/ob2``, they -might be presented with a management interface, but walking up to +context=".some.Model" route_name="manage"/>``. If you wire things up +this way someone then walks up to for example, ``/manage/ob1/ob2``, +they might be presented with a management interface, but walking up to ``/ob1/ob2`` would present them with the default object view. There are other tricks you can pull in these hybrid configurations if you're clever (and maybe masochistic) too. diff --git a/docs/glossary.rst b/docs/glossary.rst index e649b3498..c8c07ebb4 100644 --- a/docs/glossary.rst +++ b/docs/glossary.rst @@ -409,16 +409,16 @@ Glossary internally by :mod:`repoze.bfg` to perform view lookups and other policy lookups. The ability to make use of an interface is exposed to an application programmers during :term:`view - configuration` via the ``for`` argument, the ``request_type`` + configuration` via the ``context`` argument, the ``request_type`` argument and the ``containment`` argument. Interfaces are also exposed to application developers when they make use of the - :term:`event` system. Fundamentally, :mod:`repoze.bfg` programmers - can think of an interface as something that they can attach to an - object that stamps it with a "type" unrelated to its underlying - Python type. Interfaces can also be used to describe the behavior - of an object (its methods and attributes), but unless they choose - to, :mod:`repoze.bfg` programmers do not need to understand or use - this feature of interfaces. + :term:`event` system. Fundamentally, :mod:`repoze.bfg` + programmers can think of an interface as something that they can + attach to an object that stamps it with a "type" unrelated to its + underlying Python type. Interfaces can also be used to describe + the behavior of an object (its methods and attributes), but + unless they choose to, :mod:`repoze.bfg` programmers do not need + to understand or use this feature of interfaces. event An object broadcast to zero or more :term:`subscriber` callables diff --git a/docs/narr/MyProject/myproject/configure.zcml b/docs/narr/MyProject/myproject/configure.zcml index fe9633fe1..95b7355b1 100644 --- a/docs/narr/MyProject/myproject/configure.zcml +++ b/docs/narr/MyProject/myproject/configure.zcml @@ -3,7 +3,7 @@ diff --git a/docs/narr/extending.rst b/docs/narr/extending.rst index 0f9bc433d..0b2d5a876 100644 --- a/docs/narr/extending.rst +++ b/docs/narr/extending.rst @@ -181,7 +181,7 @@ Overriding Views ~~~~~~~~~~~~~~~~~ The ZCML ```` declarations you make which *override* application -behavior will usually have the same ``for`` and ``name`` (and +behavior will usually have the same ``context`` and ``name`` (and :term:`predicate` attributes, if used) as the original. These ```` declarations will point at "new" view code. The new view code itself will usually be cut-n-paste copies of view callables from @@ -190,7 +190,7 @@ the original application with slight tweaks. For example: .. code-block:: xml :linenos: - diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst index 990290744..47495b68c 100644 --- a/docs/narr/hooks.rst +++ b/docs/narr/hooks.rst @@ -225,11 +225,11 @@ a class that implements the following interface: More than one traversal algorithm can be active at the same time. For instance, if your :term:`root factory` returns more than one type of -object conditionally, you could claim that an alternate traverser is -``for`` only one particular class or interface. When the root factory -returned an object that implemented that class or interface, a custom -traverser would be used. Otherwise, the default traverser would be -used. For example: +object conditionally, you could claim that an alternate traverser +adapter is ``for`` only one particular class or interface. When the +root factory returned an object that implemented that class or +interface, a custom traverser would be used. Otherwise, the default +traverser would be used. For example: .. code-block:: xml :linenos: diff --git a/docs/narr/hybrid.rst b/docs/narr/hybrid.rst index 14cb1d4ab..5c3775e59 100644 --- a/docs/narr/hybrid.rst +++ b/docs/narr/hybrid.rst @@ -361,12 +361,13 @@ then both the request type and the context type are "more specific" for the view registration). What it all boils down to is: if a request that matches a route -resolves to a view you don't expect it to, use the ``view_for`` -attribute of the ``route`` statement (*or* the ``for`` attribute of -the ZCML statement that also has a ``route_name`` *or* the equivalent -``for_`` parameter to the :class:`repoze.bfg.view.bfg_view` decorator -that also has a ``route_name`` parameter) to name the specific context -interface you want the route-related view to match. +resolves to a view you don't expect it to, use the ``view_context`` +attribute of the ``route`` statement (*or* the ``context`` attribute +of the ZCML statement that also has a ``route_name`` *or* the +equivalent ``context`` parameter to the +:class:`repoze.bfg.view.bfg_view` decorator that also has a +``route_name`` parameter) to name the specific context interface you +want the route-related view to match. Yes, that was as painful for me to write as it was for you to read. diff --git a/docs/narr/project.rst b/docs/narr/project.rst index 41b0bce02..02d9e30ba 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -603,12 +603,13 @@ the :term:`application registry`. It looks like so: in this file. #. Lines 6-10 register a "default view" (a view that has no ``name`` - attribute). It is ``for`` model objects that are instances of the + attribute). It is registered so that it will be found when the + :term:`context` of the request is an instance of the :class:`myproject.models.MyModel` class. The ``view`` attribute points at a Python function that does all the work for this view. - Note that the values of both the ``for`` attribute and the ``view`` - attribute begin with a single period. Names that begin with a - period are "shortcuts" which point at files relative to the + Note that the values of both the ``context`` attribute and the + ``view`` attribute begin with a single period. Names that begin + with a period are "shortcuts" which point at files relative to the :term:`package` in which the ``configure.zcml`` file lives. In this case, since the ``configure.zcml`` file lives within the :mod:`myproject` package, the shortcut ``.models.MyModel`` could diff --git a/docs/narr/security.rst b/docs/narr/security.rst index e83e2f8b8..4c86c7f5e 100644 --- a/docs/narr/security.rst +++ b/docs/narr/security.rst @@ -125,7 +125,7 @@ when invoked against a ``Blog`` context with the ``add`` permission: :linenos: @@ -320,18 +320,18 @@ The above maps the ``.views.hello_world`` view function to Python class represented by ``.models.Hello`` when the *view name* is ``hello.html``. -.. note:: Values prefixed with a period (``.``) for the ``for`` and - ``view`` attributes of a ``view`` (such as those above) mean +.. note:: Values prefixed with a period (``.``) for the ``context`` + and ``view`` attributes of a ``view`` (such as those above) mean "relative to the Python package directory in which this - :term:`ZCML` file is stored". So if the above ``view`` - declaration was made inside a ``configure.zcml`` file that lived in - the ``hello`` package, you could replace the relative - ``.models.Hello`` with the absolute ``hello.models.Hello``; - likewise you could replace the relative ``.views.hello_world`` with - the absolute ``hello.views.hello_world``. Either the relative or - absolute form is functionally equivalent. It's often useful to use - the relative form, in case your package's name changes. It's also - shorter to type. + :term:`ZCML` file is stored". So if the above ``view`` declaration + was made inside a ``configure.zcml`` file that lived in the + ``hello`` package, you could replace the relative ``.models.Hello`` + with the absolute ``hello.models.Hello``; likewise you could + replace the relative ``.views.hello_world`` with the absolute + ``hello.views.hello_world``. Either the relative or absolute form + is functionally equivalent. It's often useful to use the relative + form, in case your package's name changes. It's also shorter to + type. You can also declare a *default view* for a model type: @@ -339,7 +339,7 @@ You can also declare a *default view* for a model type: :linenos: @@ -348,13 +348,13 @@ found and there is no *view name* associated with the result of :term:`traversal`, the *default view* is the view that is used. You can also declare that a view is good for any model type by using -the special ``*`` character in the ``for`` attribute: +the special ``*`` character in the ``context`` attribute: .. code-block:: xml :linenos: @@ -381,7 +381,7 @@ For better locality of reference, use the :class:`repoze.bfg.view.bfg_view` decorator to associate your view functions with URLs instead of using :term:`ZCML` for the same purpose. :class:`repoze.bfg.view.bfg_view` can be used to associate -``for``, ``name``, ``permission`` and ``request_method``, +``context``, ``name``, ``permission`` and ``request_method``, ``containment``, ``request_param`` and ``request_type``, ``attr``, ``renderer``, ``wrapper``, ``xhr``, ``accept``, and ``header`` information -- as done via the equivalent ZCML -- with a function that @@ -449,7 +449,7 @@ An example might reside in a bfg application module ``views.py``: from repoze.bfg.view import bfg_view from repoze.bfg.chameleon_zpt import render_template_to_response - @bfg_view(name='my_view', request_method='POST', for_=MyModel, + @bfg_view(name='my_view', request_method='POST', context=MyModel, permission='read', renderer='templates/my.pt') def my_view(request): return {'a':1} @@ -461,7 +461,7 @@ your application registry: :linenos: @@ -922,7 +924,7 @@ You can configure a view to use the JSON renderer in ZCML by naming :linenos: @@ -1469,11 +1471,11 @@ object. For example (ZCML): In this case, ``.models.Root`` refers to the class of which your :mod:`repoze.bfg` application's root object is an instance. -.. note:: You can also give a ``for`` of ``*`` if you want the name - ``static`` to be accessible as the static view against any model. - This will also allow ``/static/foo.js`` to work, but it will allow - for ``/anything/static/foo.js`` too, as long as ``anything`` itself - is resolvable. +.. note:: You can also give a ``context`` of ``*`` if you want the + name ``static`` to be accessible as the static view against any + model. This will also allow ``/static/foo.js`` to work, but it + will allow for ``/anything/static/foo.js`` too, as long as + ``anything`` itself is resolvable. .. note:: To ensure that model objects contained in the root don't "shadow" your static view (model objects take precedence during diff --git a/docs/tutorials/bfgwiki/basiclayout.rst b/docs/tutorials/bfgwiki/basiclayout.rst index 5934799b9..f0bf8ced8 100644 --- a/docs/tutorials/bfgwiki/basiclayout.rst +++ b/docs/tutorials/bfgwiki/basiclayout.rst @@ -30,12 +30,13 @@ following: #. *Line 4*. Boilerplate, the comment explains. -#. *Lines 6-10*. Register a ```` that is ``for`` a class. - ``.views.my_view`` is a *function* we write (generated by the - ``bfg_zodb`` template) that is given a ``context`` and a - ``request`` and which returns a dictionary. The ``renderer`` tag - indicates that the ``templates/mytemplate.pt`` template should be - used to turn the dictionary returned by the view into a response. +#. *Lines 6-10*. Register a ```` that names a ``context`` type + that is a class. ``.views.my_view`` is a *function* we write + (generated by the ``bfg_zodb`` template) that is given a + ``context`` object and a ``request`` and which returns a + dictionary. The ``renderer`` tag indicates that the + ``templates/mytemplate.pt`` template should be used to turn the + dictionary returned by the view into a response. ``templates/mytemplate.pt`` is a *relative* path: it names the ``mytemplate.pt`` file which lives in the ``templates`` subdirectory of the directory in which this ``configure.zcml`` diff --git a/docs/tutorials/bfgwiki/src/authorization/tutorial/login.py b/docs/tutorials/bfgwiki/src/authorization/tutorial/login.py index d9d65bdca..08b3db359 100644 --- a/docs/tutorials/bfgwiki/src/authorization/tutorial/login.py +++ b/docs/tutorials/bfgwiki/src/authorization/tutorial/login.py @@ -9,7 +9,7 @@ from repoze.bfg.security import forget from tutorial.models import Wiki from tutorial.security import USERS -@bfg_view(for_=Wiki, name='login', renderer='templates/login.pt') +@bfg_view(context=Wiki, name='login', renderer='templates/login.pt') def login(context, request): login_url = model_url(context, request, 'login') referrer = request.url @@ -36,7 +36,7 @@ def login(context, request): password = password, ) -@bfg_view(for_=Wiki, name='logout') +@bfg_view(context=Wiki, name='logout') def logout(context, request): headers = forget(request) return HTTPFound(location = model_url(context, request), diff --git a/docs/tutorials/bfgwiki/src/authorization/tutorial/views.py b/docs/tutorials/bfgwiki/src/authorization/tutorial/views.py index 26a44fcda..17ca01566 100644 --- a/docs/tutorials/bfgwiki/src/authorization/tutorial/views.py +++ b/docs/tutorials/bfgwiki/src/authorization/tutorial/views.py @@ -14,11 +14,11 @@ from tutorial.models import Wiki # regular expression used to find WikiWords wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") -@bfg_view(for_=Wiki, permission='view') +@bfg_view(context=Wiki, permission='view') def view_wiki(context, request): return HTTPFound(location = model_url(context, request, 'FrontPage')) -@bfg_view(for_=Page, renderer='templates/view.pt', permission='view') +@bfg_view(context=Page, renderer='templates/view.pt', permission='view') def view_page(context, request): wiki = context.__parent__ @@ -41,7 +41,7 @@ def view_page(context, request): return dict(page = context, content = content, edit_url = edit_url, logged_in = logged_in) -@bfg_view(for_=Wiki, name='add_page', renderer='templates/edit.pt', +@bfg_view(context=Wiki, name='add_page', renderer='templates/edit.pt', permission='edit') def add_page(context, request): name = request.subpath[0] @@ -61,7 +61,7 @@ def add_page(context, request): return dict(page = page, save_url = save_url, logged_in = logged_in) -@bfg_view(for_=Page, name='edit_page', renderer='templates/edit.pt', +@bfg_view(context=Page, name='edit_page', renderer='templates/edit.pt', permission='edit') def edit_page(context, request): if 'form.submitted' in request.params: diff --git a/docs/tutorials/bfgwiki/src/basiclayout/tutorial/configure.zcml b/docs/tutorials/bfgwiki/src/basiclayout/tutorial/configure.zcml index e15d3a65d..04e7d908d 100644 --- a/docs/tutorials/bfgwiki/src/basiclayout/tutorial/configure.zcml +++ b/docs/tutorials/bfgwiki/src/basiclayout/tutorial/configure.zcml @@ -4,7 +4,7 @@ diff --git a/docs/tutorials/bfgwiki/src/models/tutorial/configure.zcml b/docs/tutorials/bfgwiki/src/models/tutorial/configure.zcml index 867b9020c..149eff13d 100644 --- a/docs/tutorials/bfgwiki/src/models/tutorial/configure.zcml +++ b/docs/tutorials/bfgwiki/src/models/tutorial/configure.zcml @@ -4,7 +4,7 @@ diff --git a/docs/tutorials/bfgwiki/src/viewdecorators/tutorial/views.py b/docs/tutorials/bfgwiki/src/viewdecorators/tutorial/views.py index a5d8f6b6a..08489f4ef 100644 --- a/docs/tutorials/bfgwiki/src/viewdecorators/tutorial/views.py +++ b/docs/tutorials/bfgwiki/src/viewdecorators/tutorial/views.py @@ -11,11 +11,11 @@ from tutorial.models import Wiki # regular expression used to find WikiWords wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") -@bfg_view(for_=Wiki) +@bfg_view(context=Wiki) def view_wiki(context, request): return HTTPFound(location = model_url(context, request, 'FrontPage')) -@bfg_view(for_=Page, renderer='templates/view.pt') +@bfg_view(context=Page, renderer='templates/view.pt') def view_page(context, request): wiki = context.__parent__ @@ -34,7 +34,7 @@ def view_page(context, request): edit_url = model_url(context, request, 'edit_page') return dict(page = context, content = content, edit_url = edit_url) -@bfg_view(for_=Wiki, name='add_page', renderer='templates/edit.pt') +@bfg_view(context=Wiki, name='add_page', renderer='templates/edit.pt') def add_page(context, request): name = request.subpath[0] if 'form.submitted' in request.params: @@ -50,7 +50,7 @@ def add_page(context, request): page.__parent__ = context return dict(page = page, save_url = save_url) -@bfg_view(for_=Page, name='edit_page', renderer='templates/edit.pt') +@bfg_view(context=Page, name='edit_page', renderer='templates/edit.pt') def edit_page(context, request): if 'form.submitted' in request.params: context.data = request.params['body'] diff --git a/docs/tutorials/bfgwiki/src/views/tutorial/configure.zcml b/docs/tutorials/bfgwiki/src/views/tutorial/configure.zcml index 038677bfc..d265d63e4 100644 --- a/docs/tutorials/bfgwiki/src/views/tutorial/configure.zcml +++ b/docs/tutorials/bfgwiki/src/views/tutorial/configure.zcml @@ -9,25 +9,25 @@ /> @@ -75,18 +76,18 @@ The decorator above the ``view_page`` function will be: .. code-block:: python :linenos: - @bfg_view(for_=Page, renderer='templates/view.pt') + @bfg_view(context=Page, renderer='templates/view.pt') -This indicates that the view is "for" the Page class and has the -*empty* view_name (indicating the :term:`default view` for the Page -class). After injecting this decorator, we can now *remove* the -following from our ``configure.zcml`` file: +This indicates that the view is for the Page class and has the *empty* +view_name (indicating the :term:`default view` for the Page class). +After injecting this decorator, we can now *remove* the following from +our ``configure.zcml`` file: .. code-block:: xml :linenos: @@ -102,9 +103,9 @@ The decorator above the ``add_page`` function will be: .. code-block:: python :linenos: - @bfg_view(for_=Wiki, name='add_page', renderer='templates/edit.pt') + @bfg_view(context=Wiki, name='add_page', renderer='templates/edit.pt') -This indicates that the view is "for" the Wiki class and has the +This indicates that the view is for the Wiki class and has the ``add_page`` view_name. After injecting this decorator, we can now *remove* the following from our ``configure.zcml`` file: @@ -112,7 +113,7 @@ This indicates that the view is "for" the Wiki class and has the :linenos: @@ -453,7 +455,7 @@ Examples :linenos: @@ -602,7 +604,7 @@ Attributes .. note:: This feature is new as of :mod:`repoze.bfg` 1.2. -``view_for`` +``view_context`` The :term:`dotted Python name` to a class or an interface that the :term:`context` of the view should match for the view named by the @@ -613,7 +615,8 @@ Attributes If the ``view`` attribute is not provided, this attribute has no effect. - This attribute can also be spelled as ``for``. + This attribute can also be spelled as ``view_for`` or ``for_``; + these are valid older spellings. ``view_permission`` diff --git a/repoze/bfg/configuration.py b/repoze/bfg/configuration.py index bce8be232..5fd5b5b1b 100644 --- a/repoze/bfg/configuration.py +++ b/repoze/bfg/configuration.py @@ -431,7 +431,8 @@ class Configurator(object): request_type=None, route_name=None, request_method=None, request_param=None, containment=None, attr=None, renderer=None, wrapper=None, xhr=False, accept=None, - header=None, path_info=None, custom_predicates=(), _info=u''): + header=None, path_info=None, custom_predicates=(), + context=None, _info=u''): """ Add a :term:`view configuration` to the current configuration state. Arguments to ``add_view`` are broken down below into *predicate* arguments and *non-predicate* @@ -535,7 +536,7 @@ class Configurator(object): The :term:`view name`. Read :ref:`traversal_chapter` to understand the concept of a view name. - for + context An object representing Python class that the :term:`context` must be an instance of, *or* the :term:`interface` that the @@ -543,7 +544,9 @@ class Configurator(object): found and called. This predicate is true when the :term:`context` is an instance of the represented class or if the :term:`context` provides the represented interface; - it is otherwise false. + it is otherwise false. This argument may also be provided + to ``add_view`` as ``for_`` (an older, still-supported + spelling). route_name @@ -701,12 +704,16 @@ class Configurator(object): derived_view = self._derive_view(view, permission, predicates, attr, renderer, wrapper, name, accept) - r_for_ = for_ + + if context is None: + context = for_ + + r_context = context r_request_type = request_type - if r_for_ is None: - r_for_ = Interface - if not IInterface.providedBy(r_for_): - r_for_ = implementedBy(r_for_) + if r_context is None: + r_context = Interface + if not IInterface.providedBy(r_context): + r_context = implementedBy(r_context) if not IInterface.providedBy(r_request_type): r_request_type = implementedBy(r_request_type) @@ -733,7 +740,7 @@ class Configurator(object): old_view = None for view_type in (IView, ISecuredView, IMultiView): - old_view = registered((r_for_, r_request_type), view_type, name) + old_view = registered((r_context, r_request_type), view_type, name) if old_view is not None: break @@ -745,7 +752,7 @@ class Configurator(object): view_iface = ISecuredView else: view_iface = IView - self.registry.registerAdapter(derived_view, (for_, request_type), + self.registry.registerAdapter(derived_view, (context, request_type), view_iface, name, info=_info) else: # XXX we could try to be more efficient here and register @@ -763,8 +770,8 @@ class Configurator(object): for view_type in (IView, ISecuredView): # unregister any existing views self.registry.adapters.unregister( - (r_for_, r_request_type), view_type, name=name) - self.registry.registerAdapter(multiview, (for_, request_type), + (r_context, r_request_type), view_type, name=name) + self.registry.registerAdapter(multiview, (context, request_type), IMultiView, name, info=_info) def add_route(self, name, path, view=None, view_for=None, @@ -777,7 +784,8 @@ class Configurator(object): view_containment=None, view_attr=None, renderer=None, view_renderer=None, view_header=None, view_accept=None, view_xhr=False, - view_path_info=None, _info=u''): + view_path_info=None, view_context=None, + _info=u''): """ Add a :term:`route configuration` to the current configuration state, as well as possibly a :term:`view configuration` to be used to specify a :term:`view callable` @@ -910,7 +918,7 @@ class Configurator(object): callable when this route matches. e.g. ``mypackage.views.my_view``. - view_for + view_context A reference to a class or an :term:`interface` that the :term:`context` of the view should match for the view named @@ -921,7 +929,7 @@ class Configurator(object): If the ``view`` argument is not provided, this argument has no effect. - This attribute can also be spelled as ``for_``. + This attribute can also be spelled as ``for_`` or ``view_for``. view_permission @@ -1017,12 +1025,15 @@ class Configurator(object): request_iface, IRouteRequest, name=name) if view: - view_for = view_for or for_ + if view_context is None: + view_context = view_for + if view_context is None: + view_context = for_ view_permission = view_permission or permission view_renderer = view_renderer or renderer self.add_view( permission=view_permission, - for_=view_for, + context=view_context, view=view, name='', route_name=name, diff --git a/repoze/bfg/paster_templates/alchemy/+package+/configure.zcml b/repoze/bfg/paster_templates/alchemy/+package+/configure.zcml index 1c8f26e62..40b863c62 100644 --- a/repoze/bfg/paster_templates/alchemy/+package+/configure.zcml +++ b/repoze/bfg/paster_templates/alchemy/+package+/configure.zcml @@ -4,13 +4,13 @@ diff --git a/repoze/bfg/paster_templates/starter/+package+/configure.zcml b/repoze/bfg/paster_templates/starter/+package+/configure.zcml index 038f04da4..e83dd3933 100644 --- a/repoze/bfg/paster_templates/starter/+package+/configure.zcml +++ b/repoze/bfg/paster_templates/starter/+package+/configure.zcml @@ -4,7 +4,7 @@ diff --git a/repoze/bfg/paster_templates/zodb/+package+/configure.zcml b/repoze/bfg/paster_templates/zodb/+package+/configure.zcml index 038f04da4..e83dd3933 100644 --- a/repoze/bfg/paster_templates/zodb/+package+/configure.zcml +++ b/repoze/bfg/paster_templates/zodb/+package+/configure.zcml @@ -4,7 +4,7 @@ diff --git a/repoze/bfg/tests/test_configuration.py b/repoze/bfg/tests/test_configuration.py index d8b6aca99..ba306f26b 100644 --- a/repoze/bfg/tests/test_configuration.py +++ b/repoze/bfg/tests/test_configuration.py @@ -492,7 +492,26 @@ class ConfiguratorTests(unittest.TestCase): result = wrapper(None, None) self.assertEqual(result, 'OK') + def test_add_view_context_as_class(self): + from zope.interface import implementedBy + view = lambda *arg: 'OK' + class Foo: + pass + config = self._makeOne() + config.add_view(context=Foo, view=view) + foo = implementedBy(Foo) + wrapper = self._getViewCallable(config, foo) + self.assertEqual(wrapper, view) + + def test_add_view_context_as_iface(self): + view = lambda *arg: 'OK' + config = self._makeOne() + config.add_view(context=IDummy, view=view) + wrapper = self._getViewCallable(config, IDummy) + self.assertEqual(wrapper, view) + def test_add_view_for_as_class(self): + # ``for_`` is older spelling for ``context`` from zope.interface import implementedBy view = lambda *arg: 'OK' class Foo: @@ -504,12 +523,23 @@ class ConfiguratorTests(unittest.TestCase): self.assertEqual(wrapper, view) def test_add_view_for_as_iface(self): + # ``for_`` is older spelling for ``context`` view = lambda *arg: 'OK' config = self._makeOne() config.add_view(for_=IDummy, view=view) wrapper = self._getViewCallable(config, IDummy) self.assertEqual(wrapper, view) + def test_add_view_context_trumps_for(self): + # ``for_`` is older spelling for ``context`` + view = lambda *arg: 'OK' + config = self._makeOne() + class Foo: + pass + config.add_view(context=IDummy, for_=Foo, view=view) + wrapper = self._getViewCallable(config, IDummy) + self.assertEqual(wrapper, view) + def test_add_view_register_secured_view(self): from zope.interface import Interface from repoze.bfg.interfaces import IRequest @@ -1091,6 +1121,17 @@ class ConfiguratorTests(unittest.TestCase): self.assertEqual(wrapper(None, None), 'OK') self._assertRoute(config, 'name', 'path') + def test_add_route_with_view_context(self): + config = self._makeOne() + view = lambda *arg: 'OK' + config.add_route('name', 'path', view=view, view_context=IDummy) + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable(config, IDummy, request_type) + self.assertEqual(wrapper(None, None), 'OK') + self._assertRoute(config, 'name', 'path') + wrapper = self._getViewCallable(config, IOther, request_type) + self.assertEqual(wrapper, None) + def test_add_route_with_view_for(self): config = self._makeOne() view = lambda *arg: 'OK' @@ -1102,7 +1143,7 @@ class ConfiguratorTests(unittest.TestCase): wrapper = self._getViewCallable(config, IOther, request_type) self.assertEqual(wrapper, None) - def test_add_route_with_view_for_alias(self): + def test_add_route_with_for_(self): config = self._makeOne() view = lambda *arg: 'OK' config.add_route('name', 'path', view=view, for_=IDummy) diff --git a/repoze/bfg/tests/test_view.py b/repoze/bfg/tests/test_view.py index 677c15bc8..94b896893 100644 --- a/repoze/bfg/tests/test_view.py +++ b/repoze/bfg/tests/test_view.py @@ -289,7 +289,7 @@ class TestBFGViewDecorator(unittest.TestCase): decorator = self._makeOne() self.assertEqual(decorator.name, '') self.assertEqual(decorator.request_type, None) - self.assertEqual(decorator.for_, None) + self.assertEqual(decorator.context, None) self.assertEqual(decorator.permission, None) def test_create_nondefaults(self): @@ -297,7 +297,7 @@ class TestBFGViewDecorator(unittest.TestCase): permission='foo') self.assertEqual(decorator.name, None) self.assertEqual(decorator.request_type, None) - self.assertEqual(decorator.for_, None) + self.assertEqual(decorator.context, None) self.assertEqual(decorator.permission, 'foo') def test_call_function(self): @@ -308,7 +308,7 @@ class TestBFGViewDecorator(unittest.TestCase): self.failUnless(wrapped is foo) settings = wrapped.__bfg_view_settings__[0] self.assertEqual(settings['permission'], None) - self.assertEqual(settings['for_'], None) + self.assertEqual(settings['context'], None) self.assertEqual(settings['request_type'], None) def test_call_oldstyle_class(self): @@ -319,7 +319,7 @@ class TestBFGViewDecorator(unittest.TestCase): self.failUnless(wrapped is foo) settings = wrapped.__bfg_view_settings__[0] self.assertEqual(settings['permission'], None) - self.assertEqual(settings['for_'], None) + self.assertEqual(settings['context'], None) self.assertEqual(settings['request_type'], None) def test_call_newstyle_class(self): @@ -330,7 +330,7 @@ class TestBFGViewDecorator(unittest.TestCase): self.failUnless(wrapped is foo) settings = wrapped.__bfg_view_settings__[0] self.assertEqual(settings['permission'], None) - self.assertEqual(settings['for_'], None) + self.assertEqual(settings['context'], None) self.assertEqual(settings['request_type'], None) def test_stacking(self): diff --git a/repoze/bfg/tests/test_zcml.py b/repoze/bfg/tests/test_zcml.py index b50f841c2..3cbeb05c8 100644 --- a/repoze/bfg/tests/test_zcml.py +++ b/repoze/bfg/tests/test_zcml.py @@ -117,6 +117,27 @@ class TestViewDirective(unittest.TestCase): regview = reg.adapters.lookup((IDummy, IRequest), IView, name='') self.assertEqual(regview(None, None), 'OK') + def test_context_trumps_for(self): + from repoze.bfg.threadlocal import get_current_registry + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IRequest + context = DummyContext() + reg = get_current_registry() + view = lambda *arg: 'OK' + class Foo: + pass + self._callFUT(context, 'repoze.view', for_=Foo, view=view, + context=IDummy) + actions = context.actions + self.assertEqual(len(actions), 1) + discrim = ('view', IDummy, '', None, IView, None, None, None, None, + None, False, None, None, None) + self.assertEqual(actions[0]['discriminator'], discrim) + register = actions[0]['callable'] + register() + regview = reg.adapters.lookup((IDummy, IRequest), IView, name='') + self.assertEqual(regview(None, None), 'OK') + class TestNotFoundDirective(unittest.TestCase): def setUp(self): testing.setUp() @@ -475,6 +496,63 @@ class TestRouteDirective(unittest.TestCase): wrapped = reg.adapters.lookup((Interface, request_type), IView, name='') self.failUnless(wrapped) + def test_with_view_and_view_context(self): + from repoze.bfg.threadlocal import get_current_registry + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IRouteRequest + context = DummyContext() + view = lambda *arg: 'OK' + self._callFUT(context, 'name', 'path', view=view, view_context=IDummy) + actions = context.actions + self.assertEqual(len(actions), 2) + + route_action = actions[0] + route_action['callable']() + route_discriminator = route_action['discriminator'] + self.assertEqual(route_discriminator, + ('route', 'name', False, None, None, None, None,None)) + self._assertRoute('name', 'path') + + view_action = actions[1] + reg = get_current_registry() + request_type = reg.getUtility(IRouteRequest, 'name') + view_discriminator = view_action['discriminator'] + discrim = ('view', IDummy, '', request_type, IView, None, None, None, + 'name', None, False, None, None, None) + self.assertEqual(view_discriminator, discrim) + wrapped = reg.adapters.lookup((IDummy, request_type), IView, name='') + self.failUnless(wrapped) + + def test_with_view_context_trumps_view_for(self): + from repoze.bfg.threadlocal import get_current_registry + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IRouteRequest + context = DummyContext() + view = lambda *arg: 'OK' + class Foo: + pass + self._callFUT(context, 'name', 'path', view=view, view_context=IDummy, + view_for=Foo) + actions = context.actions + self.assertEqual(len(actions), 2) + + route_action = actions[0] + route_action['callable']() + route_discriminator = route_action['discriminator'] + self.assertEqual(route_discriminator, + ('route', 'name', False, None, None, None, None,None)) + self._assertRoute('name', 'path') + + view_action = actions[1] + reg = get_current_registry() + request_type = reg.getUtility(IRouteRequest, 'name') + view_discriminator = view_action['discriminator'] + discrim = ('view', IDummy, '', request_type, IView, None, None, None, + 'name', None, False, None, None, None) + self.assertEqual(view_discriminator, discrim) + wrapped = reg.adapters.lookup((IDummy, request_type), IView, name='') + self.failUnless(wrapped) + def test_with_dotted_renderer(self): from repoze.bfg.threadlocal import get_current_registry diff --git a/repoze/bfg/view.py b/repoze/bfg/view.py index 8a33e246d..af3acb8b6 100644 --- a/repoze/bfg/view.py +++ b/repoze/bfg/view.py @@ -233,7 +233,7 @@ class bfg_view(object): from models import MyModel - @bfg_view(name='my_view', for_=MyModel, permission='read', + @bfg_view(name='my_view', context=MyModel, permission='read', route_name='site1') def my_view(context, request): return 'OK' @@ -243,7 +243,7 @@ class bfg_view(object): import views import models - config.add_view(views.my_view, for_=models.MyModel, name='my_view', + config.add_view(views.my_view, context=models.MyModel, name='my_view', permission='read', 'route_name='site1') Or might replace the following ZCML ``view`` declaration:: @@ -257,13 +257,14 @@ class bfg_view(object): /> The following arguments are supported as arguments to - ``bfg_view``: ``for_``, ``permission``, ``name``, + ``bfg_view``: ``context``, ``permission``, ``name``, ``request_type``, ``route_name``, ``request_method``, ``request_param``, ``containment``, ``xhr``, ``accept``, ``header`` and ``path_info``. - If ``for_`` is not supplied, the interface - ``zope.interface.Interface`` (matching any context) is used. + If ``context`` is not supplied, the interface + ``zope.interface.Interface`` (matching any context) is used. An alias + for ``context`` is ``for_``. If ``permission`` is not supplied, no permission is registered for this view (it's accessible by any caller). @@ -456,10 +457,10 @@ class bfg_view(object): route_name=None, request_method=None, request_param=None, containment=None, attr=None, renderer=None, wrapper=None, xhr=False, accept=None, header=None, path_info=None, - custom_predicates=()): + custom_predicates=(), context=None): self.name = name self.request_type = request_type - self.for_ = for_ + self.context = context or for_ self.permission = permission self.route_name = route_name self.request_method = request_method diff --git a/repoze/bfg/zcml.py b/repoze/bfg/zcml.py index 6467907db..c09a52fcd 100644 --- a/repoze/bfg/zcml.py +++ b/repoze/bfg/zcml.py @@ -37,11 +37,17 @@ from repoze.bfg.threadlocal import get_current_registry ###################### directives ########################## class IViewDirective(Interface): - for_ = GlobalObject( + context = GlobalObject( title=u"The interface or class this view is for.", required=False ) + for_ = GlobalObject( + title=(u"The interface or class this view is for (alternate spelling " + "of ``context``)."), + required=False + ) + permission = TextLine( title=u"Permission", description=u"The permission needed to use the view.", @@ -162,6 +168,7 @@ def view( header=None, path_info=None, custom_predicates=(), + context=None, cacheable=True, # not used, here for b/w compat < 0.8 ): @@ -178,10 +185,12 @@ def view( if renderer and '.' in renderer: renderer = path_spec(_context, renderer) + context = context or for_ + def register(): config = Configurator(reg, package=_context.package) config.add_view( - permission=permission, for_=for_, view=view, name=name, + permission=permission, context=context, view=view, name=name, request_type=request_type, route_name=route_name, request_method=request_method, request_param=request_param, containment=containment, attr=attr, renderer=renderer, @@ -189,7 +198,7 @@ def view( path_info=path_info, custom_predicates=custom_predicates, _info=_context.info) - discriminator = ['view', for_, name, request_type, IView, containment, + discriminator = ['view', context, name, request_type, IView, containment, request_param, request_method, route_name, attr, xhr, accept, header, path_info] @@ -211,9 +220,10 @@ class IRouteDirective(Interface): factory = GlobalObject(title=u'context factory', required=False) view = GlobalObject(title=u'view', required=False) - view_for = GlobalObject(title=u'view_for', required=False) - # alias for view_for + view_context = GlobalObject(title=u'view_context', required=False) + # aliases for view_context for_ = GlobalObject(title=u'for', required=False) + view_for = GlobalObject(title=u'view_for', required=False) view_permission = TextLine(title=u'view_permission', required=False) # alias for view_permission @@ -255,7 +265,7 @@ def route(_context, name, path, view=None, view_for=None, view_request_param=None, view_containment=None, view_attr=None, renderer=None, view_renderer=None, view_header=None, view_accept=None, view_xhr=False, - view_path_info=None): + view_path_info=None, view_context=None): """ Handle ``route`` ZCML directives """ # the strange ordering of the request kw args above is for b/w @@ -264,7 +274,9 @@ def route(_context, name, path, view=None, view_for=None, # in the routelist will be tried reg = get_current_registry() - view_for = view_for or for_ + if view_context is None: + view_context = view_for or for_ + view_permission = view_permission or permission view_renderer = view_renderer or renderer if view_renderer and '.' in view_renderer: @@ -284,7 +296,7 @@ def route(_context, name, path, view=None, view_for=None, request_param=request_param, custom_predicates=custom_predicates, view=view, - view_for=view_for, + view_context=view_context, view_permission=view_permission, view_request_method=view_request_method, view_request_param=view_request_param, @@ -315,7 +327,7 @@ def route(_context, name, path, view=None, view_for=None, reg.registerUtility(request_iface, IRouteRequest, name=name) _context.action( discriminator = ( - 'view', view_for, '', request_iface, IView, + 'view', view_context, '', request_iface, IView, view_containment, view_request_param, view_request_method, name, view_attr, view_xhr, view_accept, view_header, view_path_info), -- cgit v1.2.3