# [Zope-Checkins] CVS: StandaloneZConfig/ZConfig/doc - zconfig.tex:1.93

Fred L. Drake, Jr. fred at zope.com
Fri Feb 27 14:05:38 EST 2004

Update of /cvs-repository/StandaloneZConfig/ZConfig/doc
In directory cvs.zope.org:/tmp/cvs-serv17820

Modified Files:
zconfig.tex
Log Message:
try explaining how to use schema components to extend existing schema

=== StandaloneZConfig/ZConfig/doc/zconfig.tex 1.92 => 1.93 ===
--- StandaloneZConfig/ZConfig/doc/zconfig.tex:1.92	Wed Feb 11 16:31:46 2004
+++ StandaloneZConfig/ZConfig/doc/zconfig.tex	Fri Feb 27 14:05:38 2004
@@ -1061,6 +1061,161 @@
\end{seealso}

+\section{Using Components to Extend Schema}
+
+% XXX This section needs a lot of work, but should get people started
+% who really want to add new pieces ZConfig-configured applications.
+
+It is possible to use schema components and the \keyword{\%import}
+construct to extend the set of section types available for a specific
+configuration file, and allow the new components to be used in place
+of standard components.
+
+The key to making this work is the use of abstract section types.
+Wherever the original schema accepts an abstract type, it is possible
+to load new implementations of the abstract type and use those instead
+schema.
+
+Abstract types are generally used to represent interfaces.  Sometimes
+these are interfaces for factory objects, and sometimes not, but
+there's an interface that the new component needs to implement.  What
+interface is required should be documented in the
+\element{description} element in the \element{abstracttype} element;
+this may be by reference to an interface specified in a Python module
+or described in some other bit of documentation.
+
+The following things need to be created to make the new component
+usable from the configuration file:
+
+\begin{enumerate}
+  \item An implementation of the required interface.
+
+  \item A schema component that defines a section type that contains
+        the information needed to construct the component.
+
+  \item A datatype'' function that converts configuration data to an
+        instance of the component.
+\end{enumerate}
+
+For simplicity, let's assume that the implementation is defined by a
+Python class.
+
+The example component we build here will be in the \module{noise}
+package, but any package will do.  Components loadable using
+\keyword{\%import} must be contained in the \file{component.xml} file;
+alternate filenames may not be selected by the \keyword{\%import}
+construct.
+
+Create a ZConfig component that provides a section type to support
+your component.  The new section type must declare that it implements
+the appropriate abstract type; it should probably look something like
+this:
+
+\begin{verbatim}
+<component prefix="noise.server">
+  <import package="ZServer"/>
+
+  <sectiontype name="noise-generator"
+               implements="ZServer.server"
+               datatype=".NoiseServerFactory">
+
+    <!-- specific configuration data should be described here -->
+
+    <key name="port"
+         datatype="port-number"
+         required="yes">
+      <description>
+        Port number to listen on.
+      </description>
+    </key>
+
+    <key name="color"
+         datatype=".noise_color"
+         default="white">
+      <description>
+        Silly way to specify a noise generation algorithm.
+      </description>
+    </key>
+
+  </sectiontype>
+</component>
+\end{verbatim}
+
+This example uses one of the standard ZConfig datatypes,
+\datatype{port-number}, and requires two additional types to be
+provided by the \module{noise.server} module:
+\class{NoiseServerFactory} and \function{noise_color()}.
+
+The \function{noise_color()} function is a datatype conversion for a
+key, so it accepts a string and returns the value that should be used:
+
+\begin{verbatim}
+_noise_colors = {
+    # color -> r,g,b
+    'white': (255, 255, 255),
+    'pink':  (255, 182, 193),
+    }
+
+def noise_color(string):
+    if string in _noise_colors:
+        return _noise_colors[string]
+    else:
+        raise ValueError('unknown noise color: %r' % string)
+\end{verbatim}
+
+\class{NoiseServerFactory} is a little different, as it's the datatype
+function for a section rather than a key.  The parameter isn't a
+string, but a section value object with two attributes, \member{port}
+and \member{color}.
+
+Since the \datatype{ZServer.server} abstract type requires that the
+component returned is a factory object, the datatype function can be
+implemented at the constructor for the class of the factory object.
+(If the datatype function could select different implementation
+classes based on the configuration values, it makes more sense to use
+a simple function that returns the appropriate implementation.)
+
+A class that implements this datatype might look like this:
+
+\begin{verbatim}
+from ZServer.datatypes import ServerFactory
+from noise.generator import WhiteNoiseGenerator, PinkNoiseGenerator
+
+class NoiseServerFactory(ServerFactory):
+
+    def __init__(self, section):
+        # host and ip will be initialized by ServerFactory.prepare()
+        self.host = None
+        self.ip = None
+        self.port = section.port
+        self.color = section.color
+
+    def create(self):
+        if self.color == 'white':
+            generator = WhiteNoiseGenerator()
+        else:
+            generator = PinkNoiseGenerator()
+        return NoiseServer(self.ip, self.port, generator)
+\end{verbatim}
+
+You'll need to arrange for the package containing this component is
+available on Python's \code{sys.path} before the configuration file is
+loaded; this is mostly easily done by manipulating the
+\envvar{PYTHONPATH} environment variable.
+
+Your configuration file can now include the following to load and use
+
+\begin{verbatim}
+%import noise
+
+<noise-generator>
+  port 1234
+  color white
+</noise-generator>
+\end{verbatim}
+

\section{\module{ZConfig} --- Basic configuration support}