diff --git a/typology/README.txt b/typology/README.txt index 19c44aa..386233f 100644 --- a/typology/README.txt +++ b/typology/README.txt @@ -1,14 +1,15 @@ -Quickstart Instructions -======================= +A Basic API for Dynamic Typing +============================== ($Id$) +The typology package offers a basic standard API for associating +arbitrary objects with types that may then be used for controlling +execution of code, helping with search interfaces or editing of +object data. + >>> from zope.app import zapi >>> from zope.app.testing import ztapi - >>> from zope.interface import directlyProvides - -A Basic API for Dynamic Typing -============================== >>> from cybertools.typology.interfaces import IType, ITypeManager @@ -29,29 +30,59 @@ Now we want to express that any person younger than 18 years is a child, and from 18 years on a person is an adult. (Note that this test will only work until November 2017 ;-)) -The example package gives us a class for this type that we use as an -adapter to IPerson: +The example package gives us a class (AgeGroup) for this type +that we use as an adapter to IPerson. The type itself we specify as a +subclass (IAgeGroup) of IType; thus we can associate different types +to one object by providing adapters to different type interfaces. - >>> from cybertools.typology.example.person import BasicAgeGroup - >>> ztapi.provideAdapter(IPerson, IType, BasicAgeGroup) - >>> john_type = IType(persons[0]) - >>> david_type = IType(persons[1]) - >>> carla_type = IType(persons[2]) +In addition, the AgeGroup adapter makes use of an AgeGroupManager, +a global utility that does the real work. + + >>> from cybertools.typology.example.person import IAgeGroup, AgeGroup + >>> ztapi.provideAdapter(IPerson, IAgeGroup, AgeGroup) + >>> from cybertools.typology.example.person import IAgeGroupManager + >>> from cybertools.typology.example.person import AgeGroupManager + >>> ztapi.provideUtility(IAgeGroupManager, AgeGroupManager()) + + >>> john_type = IAgeGroup(persons[0]) + >>> david_type = IAgeGroup(persons[1]) + >>> carla_type = IAgeGroup(persons[2]) We can now look what the type is telling us about the persons: >>> john_type.title u'Adult' >>> john_type.token - 'contact.person.agetype.adult' + 'contact.person.agegroup.adult' >>> david_type.token - 'contact.person.agetype.adult' + 'contact.person.agegroup.adult' >>> carla_type.token - 'contact.person.agetype.child' + 'contact.person.agegroup.child' -Usually types are equal if they have the same token: +In this case (and probably a lot of others) types are considered equal +if they have the same token: >>> john_type == david_type True >>> john_type == carla_type False + +If we want to use this type information on a search form for retrieving +only persons of a certain age group we need a list of available types +(in fact that is an iterable source and, based on it, a vocabulary). + +This is where type managers come in. A type manager is a utility or +another (possibly persistent) object knowing about the available types. + + >>> typeManager = zapi.getUtility(IAgeGroupManager) + >>> types = typeManager.types + >>> [t.title for t in types] + [u'Child', u'Adult'] + >>> types[0] == carla_type + True + >>> types[1] == john_type == david_type + True + + >>> t = typeManager.getType(carla_type.token) + >>> t.title + u'Child' diff --git a/typology/example/person.py b/typology/example/person.py index b96c409..bcf14b9 100644 --- a/typology/example/person.py +++ b/typology/example/person.py @@ -23,38 +23,63 @@ cybertools.contact package $Id$ """ -# TODO: move generic stuff to type.Type class - from zope.component import adapts from zope.interface import implements from cybertools.contact.interfaces import IPerson -from cybertools.typology.interfaces import IType +from cybertools.typology.interfaces import IType, ITypeManager +from cybertools.typology.type import BaseType, TypeManager -class BasicAgeGroup(object): - implements(IType) +# interfaces + +class IAgeGroup(IType): + """ A type interface for discerning childs and adults. + """ + + +class IAgeGroupManager(ITypeManager): + """ A type manager managing age groups. + """ + + +# implementations + +class AgeGroup(BaseType): + + implements(IAgeGroup) adapts(IPerson) - def __init__(self, context): - self.context = context - - def __eq__(self, other): - return self.token == other.token - # IType attributes @property def title(self): - return self.isChild() and u'Child' or u'Adult' + return self.isChild and u'Child' or u'Adult' @property - def token(self): return 'contact.person.agetype.' + str(self.title.lower()) + def token(self): return 'contact.person.agegroup.' + str(self.title.lower()) + + # helpers @property - def tokenForSearch(self): return self.token - - # helper methods - def isChild(self): return self.context.age < 18.0 + +class AgeGroupTypeInfo(AgeGroup): + """ Age group type info object with fixed (not computed) isChild property. + """ + + isChild = None + + def __init__(self, isChild): + self.isChild = isChild + + +class AgeGroupManager(TypeManager): + + implements(IAgeGroupManager) + + @property + def types(self): + return tuple([AgeGroupTypeInfo(flag) for flag in (True, False)]) + diff --git a/typology/interfaces.py b/typology/interfaces.py index d2fe8b6..564deb0 100644 --- a/typology/interfaces.py +++ b/typology/interfaces.py @@ -29,8 +29,9 @@ from zope.interface import Interface, Attribute class IType(Interface): - """ Associated with an object (typically as an adapter) giving - information about the object's type. + """ A collection of informations about a type; may be associated + with an object (typically as an adapter) specifying the object's + type. """ title = schema.TextLine(title=u'Title', @@ -44,8 +45,8 @@ class IType(Interface): description=u'A fairly unique token that may be used ' 'e.g. for identifying types via a catalog index') interfaceToProvide = GlobalObject(title=u'Interface to Provide', - description=u'An (optional) interface that objects of this ' - 'type will provide') + description=u'An (optional) interface (or schema) that ' + 'objects of this type will provide') factory = GlobalObject(title=u'Factory', description=u'A factory (or class) that may be used for ' 'creating an object of this type') @@ -57,10 +58,38 @@ class IType(Interface): title=u'Type Provider', description=u'A usually long-living object that corresponds ' 'to the type. Note that this object need not ' - 'provide the IType interface itself') + 'provide the IType interface itself but it ' + 'should be adaptable to ITypeProvider') + # possible extensions: + # subTypes + # parentTypes class ITypeManager(Interface): """ A utility or utility-like object (e.g. a container) that may manage (store, retrieve, assign) types. """ + + types = schema.Tuple(schema.Object(IType), unique=True, + title=u'Types', + description=u'A sequence of type objects managed by ' + 'this type manager') + + def listTypes(**criteria): + """ Return a sequence of type objects probably restricted via + a set of query criteria. + """ + + def getType(token): + """ Return a type object belonging to the token given. + """ + + +class ITypeProvider(Interface): + """ An object (probably used as an adapter) that may provide a + certain type object. + """ + + def getType(): + """ Return the type object this type provider provides. + """ diff --git a/typology/type.py b/typology/type.py new file mode 100644 index 0000000..6a0b1f3 --- /dev/null +++ b/typology/type.py @@ -0,0 +1,69 @@ +# +# Copyright (c) 2006 Helmut Merz helmutm@cy55.de +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +""" +Abstract base classes for type management. + +$Id$ +""" + +from zope.interface import implements +from cybertools.typology.interfaces import IType, ITypeManager + + +class BaseType(object): + + implements(IType) + + def __init__(self, context): + self.context = context + + def __eq__(self, other): + return self.token == other.token + + title = 'BaseType' + + @property + def token(self): return self.title.lower() + + @property + def tokenForSearch(self): return self.token + + interfaceToProvide = None + factory = None + defaultContainer = None + typeProvider = None + + +class TypeManager(object): + + implements(ITypeManager) + + @property + def types(self): + return (BaseType(None),) + + def listTypes(self, **criteria): + return self.types + + def getType(self, token): + for t in self.types: + if t.token == token: + return t + raise ValueError('Unrecognized token: ' + token) +