Chris McDonough
2011-01-18 bee62477293fc2c06c9fde4b28cbf35fbfcbaadb
modernize design defense after zcml externalization
1 files modified
113 ■■■■■ changed files
docs/designdefense.rst 113 ●●●●● patch | view | raw | blame | history
docs/designdefense.rst
@@ -21,19 +21,20 @@
it includes more than one way to resolve a URL to a :term:`view callable`:
via :term:`url dispatch` or :term:`traversal`.  Multiple methods of
configuration exist: :term:`imperative configuration`, :term:`configuration
decoration`, and :term:`ZCML`. It works with multiple different kinds of
persistence and templating systems.  And so on.  However, the existence of
most of these overlapping ways to do things are not without reason and
purpose: we have a number of audiences to serve, and we believe that TIMTOWTI
at the web framework level actually *prevents* a much more insidious and
harmful set of duplication at higher levels in the Python web community.
decoration`, and :term:`ZCML` (optionally via :term:`pyramid_zcml`). It works
with multiple different kinds of persistence and templating systems.  And so
on.  However, the existence of most of these overlapping ways to do things
are not without reason and purpose: we have a number of audiences to serve,
and we believe that TIMTOWTI at the web framework level actually *prevents* a
much more insidious and harmful set of duplication at higher levels in the
Python web community.
:app:`Pyramid` began its life as :mod:`repoze.bfg`, written by a team of
people with many years of prior :ref:`Zope` experience.  The idea of
:term:`traversal`, the usage of :term:`ZCML` and the way :term:`view lookup`
works was stolen entirely from Zope.  The authorization subsystem provided by
:app:`Pyramid` is a derivative of Zope's.  The idea that an application can
be *extended* without forking is also a Zope derivative.
:term:`traversal` and the way :term:`view lookup` works was stolen entirely
from Zope.  The authorization subsystem provided by :app:`Pyramid` is a
derivative of Zope's.  The idea that an application can be *extended* without
forking is also a Zope derivative.
Implementations of these features were *required* to allow the :app:`Pyramid`
authors to build the bread-and-butter CMS-type systems for customers in the
@@ -164,12 +165,11 @@
it works non-local.
You've now bought in to the fact that there's a registry that is just
"hanging around".  But how does the registry get populated?  Why,
:term:`ZCML` of course.  Sometimes.  Or via imperative code.  In this
particular case, however, the registration of ``ISettings`` is made by the
framework itself "under the hood": it's not present in any ZCML nor was it
performed imperatively.  This is extremely hard to comprehend.  Problem
number six.
"hanging around".  But how does the registry get populated?  Why, via code
that calls directives like ``config.add_view``.  In this particular case,
however, the registration of ``ISettings`` is made by the framework itself
"under the hood": it's not present in any user configuration.  This is
extremely hard to comprehend.  Problem number six.
Clearly there's some amount of cognitive load here that needs to be borne by
a reader of code that extends the :app:`Pyramid` framework due to its use of
@@ -323,17 +323,18 @@
- Composability.  A ZCA component registry can be populated imperatively, or
  there's an existing mechanism to populate a registry via the use of a
  configuration file (ZCML).  We didn't need to write a frontend from scratch
  to make use of configuration-file-driven registry population.
  configuration file (ZCML, via :term:`pyramid_zcml`).  We didn't need to
  write a frontend from scratch to make use of configuration-file-driven
  registry population.
- Pluggability.  Use of the ZCA registry allows for framework extensibility
  via a well-defined and widely understood plugin architecture.  As long as
  framework developers and extenders understand the ZCA registry, it's
  possible to extend :app:`Pyramid` almost arbitrarily.  For example, it's
  relatively easy to build a ZCML directive that registers several views "all
  at once", allowing app developers to use that ZCML directive as a "macro"
  in code that they write.  This is somewhat of a differentiating feature
  from other (non-Zope) frameworks.
  relatively easy to build a directive that registers several views "all at
  once", allowing app developers to use that directive as a "macro" in code
  that they write.  This is somewhat of a differentiating feature from other
  (non-Zope) frameworks.
- Testability.  Judicious use of the ZCA registry in framework code makes
  testing that code slightly easier.  Instead of using monkeypatching or
@@ -346,9 +347,8 @@
  for just these purposes.  The ZCA registry contains optional C code for
  this purpose which demonstrably has no (or very few) bugs.
- Ecosystem.  Many existing Zope packages can be used in
  :app:`Pyramid` with few (or no) changes due to our use of the ZCA
  registry and :term:`ZCML`.
- Ecosystem.  Many existing Zope packages can be used in :app:`Pyramid` with
  few (or no) changes due to our use of the ZCA registry.
Conclusion
++++++++++
@@ -366,13 +366,12 @@
either need to get familiar with how we're using the ZCA registry or you'll
need to use only the documented APIs; that's why we document them as APIs.
If you *extend* or *develop* :app:`Pyramid` (create new ZCML directives, use
some of the more obscure "ZCML hooks" as described in :ref:`hooks_chapter`,
or work on the :app:`Pyramid` core code), you will be faced with needing to
understand at least some ZCA concepts.  In some places it's used unabashedly,
and will be forever.  We know it's quirky, but it's also useful and
fundamentally understandable if you take the time to do some reading about
it.
If you *extend* or *develop* :app:`Pyramid` (create new directives, use some
of the more obscure "hooks" as described in :ref:`hooks_chapter`, or work on
the :app:`Pyramid` core code), you will be faced with needing to understand
at least some ZCA concepts.  In some places it's used unabashedly, and will
be forever.  We know it's quirky, but it's also useful and fundamentally
understandable if you take the time to do some reading about it.
Pyramid Uses Interfaces Too Liberally
-------------------------------------
@@ -458,46 +457,6 @@
completely optional.  No ZCML is required at all to use :app:`Pyramid`, nor
any other sort of frameworky declarative frontend to application
configuration.
Pyramid Uses ZCML; ZCML is XML and I Don't Like XML
---------------------------------------------------
:term:`ZCML` is a configuration language in the XML syntax.  Due to the
"imperative configuration" feature (new in :mod:`repoze.bfg` 1.2), you don't
need to use ZCML at all.  But if you really do want to perform declarative
configuration, perhaps because you want to build an extensible application,
you may need to use and understand it.
:term:`ZCML` contains elements that are mostly singleton tags that are
called *declarations*.  For an example:
.. code-block:: xml
   :linenos:
   <route
     view=".views.my_view"
     path="/"
     name="root"
    />
This declaration associates a :term:`view` with a route pattern.
All :app:`Pyramid` declarations are singleton tags, unlike many other XML
configuration systems.  No XML *values* in ZCML are meaningful; it's always
just XML tags and attributes.  So in the very common case it's not really
very much different than an otherwise "flat" configuration format like
``.ini``, except a developer can *create* a directive that requires nesting
(none of these exist in :app:`Pyramid` itself), and multiple "sections" can
exist with the same "name" (e.g. two ``<route>`` declarations) must be able
to exist simultaneously.
You might think some other configuration file format would be better.  But
all configuration formats suck in one way or another.  I personally don't
think any of our lives would be markedly better if the declarative
configuration format used by :app:`Pyramid` were YAML, JSON, or INI.  It's
all just plumbing that you mostly cut and paste once you've progressed 30
minutes into your first project.  Folks who tend to agitate for another
configuration file format are folks that haven't yet spent that 30 minutes.
.. _model_traversal_confusion:
@@ -983,11 +942,11 @@
other more domain-specific configuration plugpoints while developing an
application.  The plugpoints you expose needn't be as coarse as the ones
provided automatically by :app:`Pyramid` itself.  For example, you might
compose your own :term:`ZCML` directive that configures a set of views for a
prebaked purpose (e.g. ``restview`` or somesuch) , allowing other people to
refer to that directive when they make declarations in the ``configure.zcml``
of their customization package.  There is a cost for this: the developer of
an application that defines custom plugpoints for its deployers will need to
compose your own directive that configures a set of views for a prebaked
purpose (e.g. ``restview`` or somesuch) , allowing other people to refer to
that directive when they make declarations in the ``includeme`` of their
customization package.  There is a cost for this: the developer of an
application that defines custom plugpoints for its deployers will need to
understand the ZCA or he will need to develop his own similar extensibility
system.