summaryrefslogtreecommitdiff
path: root/docs/narr/extending.rst
blob: b96fb5df4492b5315039de0b5012e791f457264a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
.. _extending_chapter:

Extending An Existing :mod:`repoze.bfg` Application
===================================================

If the developer of a :mod:`repoze.bfg` application has obeyed certain
constraints while building the application, a third party should be able to
change the behavior of that application without needing to modify the actual
source code that makes up the application.  The behavior of a
:mod:`repoze.bfg` application that obeys these constraints can be
*overridden* or *extended* without modification.

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 ``<scan>`` 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 ``<include
file="otherfile.zcml"/>`` 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".

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.

Extending an Application Which Possesses ``@bfg_view`` Decorators
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If you've inherited a :mod:`repoze.bfg` application which uses
``@bfg_view`` decorators, 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`.

Once this is done, you should be able to extend or modify the
application like any other (follow the instructions below).

Extending an Application Which Does Not Possess ``@bfg_view`` Decorators
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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:

- Create a new Python package.  The easiest way to do this is to
  create a new :mod:`repoze.bfg` application using the "paster"
  template mechanism.  See :ref:`creating_a_project` for more
  information.

- Install the new package into the same Python environment as the
  original application (e.g. ``python setup.py develop`` or ``python
  setup.py install``).

- 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.  ``<include package="theoriginalapp"/>``.
  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
  ``<include>`` statement, e.g. ``<include package="theoriginalapp"
  file="views.zcml"/>``.

- On a line in the new package's ``configure.zcml`` file that falls
  after (XML-ordering-wise) the all ``include`` statements of original
  package ZCML, put an ``includeOverrides`` statement which identifies
  *another* ZCML file within the new package (for example
  ``<includeOverrides file="overrides.zcml"/>``.

- Create an ``overrides.zcml`` file within the new package.  The
  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.

  The ZCML ``<view>`` statements you make which *override* application
  behavior will usually have the same ``for`` and ``name`` (and
  ``request_type`` if used) as the original.  These ``<view>>``
  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::

    <view for="theoriginalapplication.models.SomeModel"
          name="theview"
          view=".views.a_view_that_does_something_slightly_different"
     />

  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.

- 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.

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,
omitting others. But sometimes it's not.  For brute force purposes,
when you're getting ``view`` or ``route`` registrations that you don't
actually want in your overridden application, it's always appropriate
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
``<include package=""/>`` ZCML statement in the overriding package's
``configure.zcml``.