Skip to content

Commit

Permalink
Move out component bits of repoze.plugin into repoze.component.
Browse files Browse the repository at this point in the history
  • Loading branch information
mcdonc committed Jun 14, 2009
0 parents commit aaab96b
Show file tree
Hide file tree
Showing 21 changed files with 2,033 additions and 0 deletions.
7 changes: 7 additions & 0 deletions CHANGES.txt
@@ -0,0 +1,7 @@
repoze.component Changelog
==========================

0.1 (unreleased)
----------------

- Initial release.
3 changes: 3 additions & 0 deletions COPYRIGHT.txt
@@ -0,0 +1,3 @@
Copyright (c) 2009 Agendaless Consulting and Contributors.
(http://www.agendaless.com), All Rights Reserved

41 changes: 41 additions & 0 deletions LICENSE.txt
@@ -0,0 +1,41 @@
License

A copyright notice accompanies this license document that identifies
the copyright holders.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

1. Redistributions in source code must retain the accompanying
copyright notice, this list of conditions, and the following
disclaimer.

2. Redistributions in binary form must reproduce the accompanying
copyright notice, this list of conditions, and the following
disclaimer in the documentation and/or other materials provided
with the distribution.

3. Names of the copyright holders must not be used to endorse or
promote products derived from this software without prior
written permission from the copyright holders.

4. If any files are modified, you must cause the modified files to
carry prominent notices stating that you changed the files and
the date of any change.

Disclaimer

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND
ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.

11 changes: 11 additions & 0 deletions README.txt
@@ -0,0 +1,11 @@
repoze.component README
=======================

:mod:`repoze.component` is a package that software developers can use
to provide configurability and pluggability to their applications.
:mod:`repoze.plugin` provides a generalized indirection mechanism
which can be used to provide plugin points to integrators or other
developers who may wish to provide alternate implementations of
application logic or configuration values.

See docs/index.rst for more information.
Binary file added docs/.static/logo_hi.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 22 additions & 0 deletions docs/.static/repoze.css
@@ -0,0 +1,22 @@
@import url('default.css');
body {
background-color: #006339;
}

div.document {
background-color: #dad3bd;
}

div.sphinxsidebar h3, h4, h5, a {
color: #127c56 !important;
}

div.related {
color: #dad3bd !important;
background-color: #00744a;
}

div.related a {
color: #dad3bd !important;
}

70 changes: 70 additions & 0 deletions docs/Makefile
@@ -0,0 +1,70 @@
# Makefile for Sphinx documentation
#

# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =

# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d .build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .

.PHONY: help clean html web pickle htmlhelp latex changes linkcheck

help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " pickle to make pickle files (usable by e.g. sphinx-web)"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " changes to make an overview over all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"

clean:
-rm -rf .build/*

html:
mkdir -p .build/html .build/doctrees
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) .build/html
@echo
@echo "Build finished. The HTML pages are in .build/html."

pickle:
mkdir -p .build/pickle .build/doctrees
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) .build/pickle
@echo
@echo "Build finished; now you can process the pickle files or run"
@echo " sphinx-web .build/pickle"
@echo "to start the sphinx-web server."

web: pickle

htmlhelp:
mkdir -p .build/htmlhelp .build/doctrees
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) .build/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in .build/htmlhelp."

latex:
mkdir -p .build/latex .build/doctrees
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) .build/latex
@echo
@echo "Build finished; the LaTeX files are in .build/latex."
@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
"run these through (pdf)latex."

changes:
mkdir -p .build/changes .build/doctrees
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) .build/changes
@echo
@echo "The overview file is in .build/changes."

linkcheck:
mkdir -p .build/linkcheck .build/doctrees
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) .build/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in .build/linkcheck/output.txt."
183 changes: 183 additions & 0 deletions docs/adapter.rst
@@ -0,0 +1,183 @@
Using :mod:`repoze.component` as an Adapter System
==================================================

We've seen the basic registration and lookup facilities of a
:mod:`repoze.component` registry. You can provide additional
functionality to your applications if you use it as an "adapter
registry". Using a component registry as an adapter registry makes it
possible to use the :mod:`repoze.component` component registry for the
same purposes as something like :term:`zope.component`.

The extra methods exposed by a :mod:`repoze.component` registry that
allow you to treat it as an adapter regstry are ``resolve`` and
``adapt``. The ``resolve`` method simply accepts a provides value and
sequence of *objects that supply component types* and returns a
matching component. The ``adapt`` method does exactly what resolve
does, but it additionally uses the component as a factory, passing
each ``requires`` value to it as a positional argument and returns the
result.

.. note:: If you hold your head just right and squint at it hard
enough, you might consider :mod:`repoze.component` software that makes
it possible to do a form of `aspect-oriented programming
<http://en.wikipedia.org/wiki/Aspect-oriented_programming>`_ in
Python. However, you need to know nothing about aspect-oriented
programming to use it (the author doesn't really know anything
about "proper" aspect-oriented programming either, but he's told
that it's not dissimlar to the mechanisms exposed by
:mod:`repoze.component`).

"Requires" Objects Supply Component Types
-----------------------------------------

Object used as "requires" arguments to the ``resolve`` and ``adapts``
methods of a component registry usually supply a component type. This
is done by adding a ``__component_type__`` attribute to objects passed
to these methods.

The component type must be stored on those objects as the
``__component_type__`` attribute. The ``__component_type__`` may be a
single object (usually a string) or it may be a sequence of objects.
The object is assumed to be a single object if the
``__component_type__`` does not have an ``__iter__`` method. For
example, the following is legal:

.. code-block:: python
class MyObject(object):
__component_type__ = 'mytype'
The followng is also legal.

.. code-block:: python
class MyObject(object):
__component_type__ = ('mytype', 'anothertype')
Likewise, it's also legal to do:

.. code-block:: python
class MyObject(object):
__component_type__ = ['mytype', 'anothertype']
Classes or instances can carry component types:

.. code-block:: python
class MyObject(object):
pass
obj = MyObject()
obj.__component_type__ = ('mytype', 'anothertype')
How :mod:`repoze.component` Computes an Effecive Component Type for a Requires Object
-------------------------------------------------------------------------------------

When a component type is computed for an object, the object is
searched in the following order. All values are collected and used to
construct the final "requires" argument used.

- The object itself is checked for the ``__component_type__``
attribute.

- If the object is a class, its base classes are checked in Python MRO
order for a ``__component_type__`` attribute.

- If the object is an instance, its class then its base classes are
checked in Python MRO order for a ``__component_type__`` attribute.

We'll use the following set of objects as examples:

.. code-block:: python
class A(object):
__component_type__ = ('a', 'hello')
class B(A):
__component_type__ = 'b'
class C(B):
__component_type__ = 'c'
instance = C()
instance.__component_type__ = 'i'
If "instance" is used as an argument to the ``resolve`` method of an
component registry:

- We first look at the instance to find a component type. This
finds component type ``i``.

- We look at its direct class ``C`` which finds component type ``c``.

- We look at the component type of the base classes of the ``C``
class. The B class provides component type ``b``, the ``A`` class
provides component types (``a`` and ``hello``).

Thus our "requires" argument for this particular object is ``['i',
'c', 'b', 'a', 'hello']``. Every object supplied as a "requires"
argument to either the ``resolve`` or ``adapt`` method of a component
registry has its requires values computed this way. We then find a
component based on the set of requires arguments passed in ala
:ref:`lookup_ordering`.

Comparing :mod:`repoze.component` to :term:`zope.component`
-----------------------------------------------------------

Zope and Twisted developers (and any other developer who has used
:term:`zope.component`) will find :mod:`repoze.component` familiar.
:mod:`repoze.component` steals concepts shamelessly from
:term:`zope.component`. :mod:`repoze.component` differs primarily from
:term:`zope.component` by abandoning the high-level concept of an
:term:`interface`. In :term:`zope.component`, component lookups and
registrations are done in terms of interfaces, which are very specific
kinds of Python objects. In :mod:`repoze.component`, interfaces are not
used. Instead, components (such as "adapters" and "utilities") are
registered using marker "component types", which are usually just
strings although they can be any hashable type.

.. note::

In the examples below, where a :term:`zope.component` API might
expect an interface object (e.g. the interface ``ISomething``), the
:mod:`repoze.component` API expects a compoment type (e.g. the string
``something``). Also, in the examples below, whereas
:term:`zope.component` users typically rely on APIs that consult a
"global registry", :mod:`repoze.component` provides no such facility.
Thus examples that refer to ``registry`` below refer to a plugin
registry created by parsing a configuration file (or constructed
manually).

The :mod:`repoze.component` equivalent of ``utility =
zope.component.getUtility(ISomething)`` is the following:

.. code-block:: python
utility = registry.lookup('something')
The :mod:`repoze.component` equivalent of ``implementation =
zope.component.getAdapter(context, ISomething, name='foo')`` is the
following:

.. code-block:: python
implementation = registry.adapt('something', context, name='foo')
The :mod:`repoze.component` equivalent of ``implementation =
getMultiAdapter((context1, context2), ISomething, name='foo')`` is the
following:

.. code-block:: python
implementation = registry.adapt('something', context1, context2, name='foo')
Likewise, the :mod:`repoze.component` equivalent of ``implementation =
getMultiAdapter((context1, context2, context3), ISomething,
name='foo')`` is the following:

.. code-block:: python
implementation = registry.adapt('something', context1, context2, context3,
name='foo')

0 comments on commit aaab96b

Please sign in to comment.