'.. versionadded:: 1.7
Add a :term:`view deriver` to the view pipeline. View derivers are
a feature used by extension authors to wrap views in custom code
controllable by view-specific options.
``deriver`` should be a callable conforming to the
:class:`pyramid.interfaces.IViewDeriver` interface.
``name`` should be the name of the view deriver.  There are no
restrictions on the name of a view deriver. If left unspecified, the
name will be constructed from the name of the ``deriver``.
The ``under`` and ``over`` options can be used to control the ordering
of view derivers by providing hints about where in the view pipeline
the deriver is used. Each option may be a string or a list of strings.
At least one view deriver in each, the over and under directions, must
exist to fully satisfy the constraints.
``under`` means closer to the user-defined :term:`view callable`,
and ``over`` means closer to view pipeline ingress.
The default value for ``over`` is ``rendered_view`` and ``under`` is
``decorated_view``. This places the deriver somewhere between the two
in the view pipeline. If the deriver should be placed elsewhere in the
pipeline, such as above ``decorated_view``, then you MUST also specify
``under`` to something earlier in the order, or a
``CyclicDependencyError`` will be raised when trying to sort the
derivers.
See :ref:`view_derivers` for more information.'

@action_method
def add_view_deriver(self, deriver, name=None, under=None, over=None):

    deriver = self.maybe_dotted(deriver)
    if (name is None):
        name = deriver.__name__
    if (name in (INGRESS, VIEW)):
        raise ConfigurationError(('%s    is    a    reserved    view    deriver    name' % name))
    if (under is None):
        under = 'decorated_view'
    if (over is None):
        over = 'rendered_view'
    over = as_sorted_tuple(over)
    under = as_sorted_tuple(under)
    if (INGRESS in over):
        raise ConfigurationError(('%s    cannot    be    over    INGRESS' % name))
    if ((VIEW in over) and (name != 'mapped_view')):
        over = as_sorted_tuple((over + ('mapped_view',)))
    if (VIEW in under):
        raise ConfigurationError(('%s    cannot    be    under    VIEW' % name))
    if ('mapped_view' in under):
        raise ConfigurationError(('%s    cannot    be    under    "mapped_view"' % name))
    discriminator = ('view    deriver', name)
    intr = self.introspectable('view    derivers', name, name, 'view    deriver')
    intr['name'] = name
    intr['deriver'] = deriver
    intr['under'] = under
    intr['over'] = over
    def register():
        derivers = self.registry.queryUtility(IViewDerivers)
        if (derivers is None):
            derivers = TopologicalSorter(default_before=None, default_after=INGRESS, first=INGRESS, last=VIEW)
            self.registry.registerUtility(derivers, IViewDerivers)
        derivers.add(name, deriver, before=over, after=under)
    self.action(discriminator, register, introspectables=(intr,), order=PHASE1_CONFIG)
