From 1c163c0258daddbb4c584d34fdb3b91b797fd10d Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Sat, 14 Nov 2009 06:44:20 +0000 Subject: - Improve the "Extending an Existing Application" narrative chapter. --- docs/narr/extending.rst | 212 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 153 insertions(+), 59 deletions(-) (limited to 'docs/narr/extending.rst') diff --git a/docs/narr/extending.rst b/docs/narr/extending.rst index b96fb5df4..ffe0080e7 100644 --- a/docs/narr/extending.rst +++ b/docs/narr/extending.rst @@ -13,58 +13,116 @@ source code that makes up the application. The behavior of a Rules for Building An Extensible Application -------------------------------------------- -There's only one rule you need to obey if you want to build a -maximally extensible :mod:`repoze.bfg` application: you should not use -the ``@bfg_view`` decorator or any other decorator meant to be -detected via the ZCML ```` directive. Instead, you must use -:term:`ZCML` for the equivalent purpose. :term:`ZCML` statements that -belong to an application can be "overridden" by integrators as -necessary, but decorators which perform the same tasks cannot. - -It's also often helpful for third party application "extenders" (aka -"integrators") if the ZCML that composes the configuration for an -application is broken up into separate files which do very specific -things. These more specific ZCML files can be reintegrated within the -application's main ``configure.zcml`` via ```` statements. When ZCML files contain sets of -specific statements, an integrator can avoid including any ZCML he -does not want by including only the ZCML files which contain the -registrations he needs. He is not forced to "accept everything" or -"use nothing". +We'll describe the rules that must be obeyed by the developer of a +:mod:`repoze.bfg` application if he or she wants his application to be +maximally extensible. + +Fundamental Plugpoints +~~~~~~~~~~~~~~~~~~~~~~ + +The fundamental "plug points" of an application developed using +:mod:`repoze.bfg` are *routes*, *views*, and *resources*. Routes are +declarations made using the ZCML ```` directive. Views are +declarations made using the ZCML ```` directive (or the +``@bfg_view`` decorator). Resources are files that are accessed by +repoze bfg using the :term:`pkg_resources` API such as static files +and templates. + +There's only one rule you absolutely need to obey if you want to build +a maximally extensible :mod:`repoze.bfg` application: you should not +use the ``@bfg_view`` decorator or any other decorator meant to be +detected via the ZCML ```` directive, and you mustn't configure +your :mod:`repoze.bfg` application *imperatively* by using any code +which configures the application by mutating the BFG component +registry via Python. Instead, you must use :term:`ZCML` for the +equivalent purposes. :term:`ZCML` declarations that belong to an +application can be "overridden" by integrators as necessary, but +decorators and imperative code which perform the same tasks cannot. + +In general: use only :term:`ZCML` to configure your application if +you'd like it to be extensible. + +ZCML Granularity +~~~~~~~~~~~~~~~~ + +It's also extremely helpful to third party application "extenders" +(aka "integrators") if the :term:`ZCML` that composes the +configuration for an application is broken up into separate files +which do very specific things. These more specific ZCML files can be +reintegrated within the application's main ``configure.zcml`` via +```` declarations. When ZCML files +contain sets of specific declarations, an integrator can avoid +including any ZCML he does not want by including only ZCML files which +contain the declarations he needs. He is not forced to "accept +everything" or "use nothing". + +For example, it's often useful to put all ```` declarations in +a separate ZCML file, as ```` statements have a relative +ordering that is extremely important to the application: if an +extender wants to add a route to the "middle" of the routing table, he +will always need to disuse all the routes and cut and paste the +routing configuration into his own application. It's useful for the +extender to be able to disuse just a *single* ZCML file in this case, +accepting the remainder of the configuration from other :term:`ZCML` +files in the original application. + +Granularizing ZCML is not strictly required. An extender can always +disuse *all* your ZCML, choosing instead to copy and paste it into his +own package, if necessary. However, doing so is considerate, and +allows for the best reusability. Extending an Existing Application --------------------------------- The steps for extending an existing application depend largely on -whether the application does or does not use ``@bfg_view`` decorators. +whether the application does or does not use configuration decorators +and/or imperative code. -Extending an Application Which Possesses ``@bfg_view`` Decorators -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Extending an Application Which Possesses Configuration Decorators Or Which Does Configuration Imperatively +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you've inherited a :mod:`repoze.bfg` application which uses -``@bfg_view`` decorators, one of two things may be true: +``@bfg_view`` decorators or which performs configuration imperatively, +one of two things may be true: - If you just want to *extend* the application, you can write - additional ZCML that registers more views, loading the existing - views by using the ``scan`` ZCML directive. - -- If you want to *override* one or more views in the application, - you'll unfortunately need to change the source code of the original - application, moving or copying the view declaration information out - of decorator arguments into ``view`` statements in :term:`ZCML`. + additional ZCML that registers more views or routes, loading the + existing views by using the ``scan`` ZCML directive and continuing + to use any existing imperative configuration done by the original + application. + +- If you want to *override* configuration in the application, you + *may* need to change the source code of the original application. + + If the only source of trouble is the existence of ``@bfg_view`` + decorators, you can just omit the ```` directive in the + application ZCML. This will cause the decorators to do nothing. At + this point, you will need to convert all the configuration done in + decorators into equivalent :term:`ZCML` and add that ZCML to an a + separate Python package as described in + :ref:`extending_the_application`. + + If the source of trouble is configuration done imperatively (perhaps + in the ``make_app`` function called during application startup), + you'll need to or copying configuration information out of decorator + arguments and code which does imperative configuration into + equivalent :term:`ZCML` declarations. Once this is done, you should be able to extend or modify the -application like any other (follow the instructions below). +application like any other (see :ref:`extending_the_application`). + +.. _extending_the_application: -Extending an Application Which Does Not Possess ``@bfg_view`` Decorators -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Extending an Application Which Does Not Possess Configuration Decorators or Imperative Configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To extend or override the behavior of an existing application, you will need to write some :term:`ZCML`, and perhaps some implementations of the types of things you'd like to override (such as views), which are referred to within that ZCML. -The general pattern for extending an application looks something like this: +The general pattern for extending an existing application looks +something like this: - Create a new Python package. The easiest way to do this is to create a new :mod:`repoze.bfg` application using the "paster" @@ -78,11 +136,12 @@ The general pattern for extending an application looks something like this: - Change the ``configure.zcml`` in the new package to include the original :mod:`repoze.bfg` application's ``configure.zcml`` via an include statement, e.g. ````. - Alternately, instead of including the "main" ``configure.zcml`` of - the original application include only specific ZCML files from the - original application using the ``file`` attribute of the - ```` statement, e.g. ````. + Alternately, if the original application writer anticipated + overriding some things and not others, instead of including the + "main" ``configure.zcml`` of the original application, include only + specific ZCML files from the original application using the ``file`` + attribute of the ```` statement, e.g. ````. - On a line in the new package's ``configure.zcml`` file that falls after (XML-ordering-wise) the all ``include`` statements of original @@ -94,36 +153,71 @@ The general pattern for extending an application looks something like this: statements in the ``overrides.zcml`` file will override any ZCML statements made within the original application (such as views). -- Create Python files containing views (and other overridden elements, - such as templates) as necessary, and wire these up using ZCML - registrations within the ``overrides.zcml`` file. These - registrations may extend or override the original view - registrations. +- Create Python files containing views and other overridden elements, + such as templates and static resources as necessary, and wire these + up using ZCML registrations within the ``overrides.zcml`` file. + These registrations may extend or override the original view + registrations. See :ref:`overriding_views`, + :ref:`overriding_routes` and :ref:`overriding_resources`. + +- Change the Paste ``.ini`` file that starts up the original + application. Add a ``configure_zcml`` key within the application's + section in the file which points at your *new* package's + ``configure.zcml`` file. See :ref:`environment_chapter` for more + information about this setting. + +.. _overriding_views: + +Overriding Views +~~~~~~~~~~~~~~~~~ - The ZCML ```` statements you make which *override* application - behavior will usually have the same ``for`` and ``name`` (and - ``request_type`` if used) as the original. These ``>`` - statements will point at "new" view code. The new view code itself - will usually be cut-n-paste copies of view callables from the - original application with slight tweaks. For example:: +The ZCML ```` declarations you make which *override* application +behavior will usually have the same ``for`` 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 +the original application with slight tweaks. For example:: - A similar pattern can be used to *extend* the application. Just - register a new view against some existing model type and make sure - the URLs it implies are available on some other page rendering. +A similar pattern can be used to *extend* the application with +```` declarations. Just register a new view against some +existing model type and make sure the URLs it implies are available on +some other page rendering. -- Change the Paste ``.ini`` file that starts up the original - application. Add a ``configure_zcml`` statement within the - application's section in the file which points at your *new* - package's ``configure.zcml`` file. See :ref:`environment_chapter` - for more information about this setting. +.. _overriding_routes: + +Overriding Routes +~~~~~~~~~~~~~~~~~ + +Route setup is currently typically performed in a sequence of ordered +ZCML ```` declarations. Because these declarations are ordered +relative to each other, and because this ordering is typically +important, you should retain the relative ordering of these +declarations when performing an override. Typically, this means +*copying* all the ```` declarations into an external ZCML file +and changing them as necessary. Then disinclude any ZCML from the +original application which contains the original declarations. + +.. _overriding_resources: + +Overriding Resources +~~~~~~~~~~~~~~~~~~~~ + +"Resource" files are static files on the filesystem that are +accessible within a Python *package*. An entire chapter is devoted to +resources: :ref:`resources_chapter`. Within this chapter is a section +named :ref:`overriding_resources_section`. This section of that +chapter describes in detail how to override package resources with +other resouces by using :term:`ZCML` ```` declarations. Add +such ```` declarations to your override package's +``configure.zcml`` to perform overrides. Dealing With ZCML Inclusions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +---------------------------- Sometimes it's possible to include only certain ZCML files from an application that contain only the registrations you really need, @@ -134,7 +228,7 @@ to just *not include* any ZCML file from the overridden application. Instead, just cut and paste the entire contents of the ``configure.zcml`` (and any ZCML file included by the overridden application's ``configure.zcml``) into your own package and omit the -```` ZCML statement in the overriding package's +```` ZCML declaration in the overriding package's ``configure.zcml``. -- cgit v1.2.3