[Subversion] / ObjectRoles / README.txt  

Diff of /ObjectRoles/README.txt

Parent Directory | Revision Log

version 2344, Thu Jul 12 17:17:30 2007 UTC version 2353, Sat Jul 14 14:24:39 2007 UTC
Line 2 
Line 2 
 Separating Concerns Using Object Roles  Separating Concerns Using Object Roles
 ======================================  ======================================
   
   (NEW in version 0.6: the``Registry`` base class, and the
   ``ClassRole.for_frame()`` classmethod.)
   
 In any sufficiently-sized application or framework, it's common to end up  In any sufficiently-sized application or framework, it's common to end up
 lumping a lot of different concerns into the same class.  For example, you  lumping a lot of different concerns into the same class.  For example, you
 may have business logic, persistence code, and UI all jammed into a single  may have business logic, persistence code, and UI all jammed into a single
Line 116 
Line 119 
 one (or is a new-style class with a read-only ``__dict__``), they are stored in  one (or is a new-style class with a read-only ``__dict__``), they are stored in
 a special dictionary linked to the subject via a weak reference.  a special dictionary linked to the subject via a weak reference.
   
 By default, the dictionary key is a one-element tuple containing the role  By default, the dictionary key is the role class, so there is exactly one role
 class, so that there is exactly one role instance per subject::  instance per subject::
   
     >>> aThing.__dict__      >>> aThing.__dict__
     {<class 'Persistence'>: <Persistence object at...>}      {<class 'Persistence'>: <Persistence object at...>}
Line 130 
Line 133 
 are stored under, such that more than one role instance can exist for a given  are stored under, such that more than one role instance can exist for a given
 object::  object::
   
     >>> from UserDict import UserDict      >>> class Index(Role, dict):
     >>> class Index(Role, UserDict):  
     ...     def __init__(self, subject, expression):      ...     def __init__(self, subject, expression):
     ...         self.expression = expression      ...         self.expression = expression
     ...         self.data = {}  
   
     >>> something = Thing()      >>> something = Thing()
     >>> Index(something, "x>y")["a"] = "b"      >>> Index(something, "x>y")["a"] = "b"
Line 144 
Line 145 
     >>> "a" in Index(something, "z<22")      >>> "a" in Index(something, "z<22")
     False      False
   
       >>> Index(something, "x>y")
       {'a': 'b'}
   
       >>> Index(something, "x>y").expression
       'x>y'
   
     >>> dir(something)      >>> dir(something)
     ['__doc__', '__module__', (<class 'Index'>, 'x>y'), (<class 'Index'>, 'z<22')]      ['__doc__', '__module__', (<class 'Index'>, 'x>y'), (<class 'Index'>, 'z<22')]
   
Line 214 
Line 221 
       ...        ...
     AttributeError: 'NoDict' object has no attribute '__leech__'      AttributeError: 'NoDict' object has no attribute '__leech__'
   
 Of course, if an object doesn't have a dictionary *and* isn't weak-  Of course, if an object doesn't have a dictionary *and* isn't
 referenceable, there's simply no way to store a role for it::  weak-referenceable, there's simply no way to store a role for it::
   
     >>> ob = object()      >>> ob = object()
     >>> Leech(ob)      >>> Leech(ob)
Line 505 
Line 512 
     >>> SpecialMethodRegistry(Demo).special_methods      >>> SpecialMethodRegistry(Demo).special_methods
     {'y': 55, 'x': 23}      {'y': 55, 'x': 23}
   
 (Alternately, you can pass a specific Python frame object via the ``frame``  Alternately, you can pass a specific Python frame object via the ``frame``
 keyword argument to ``for_enclosing_class()``.)  keyword argument to ``for_enclosing_class()``, or use the ``for_frame()``
   classmethod instead.  ``for_frame()`` takes a Python stack frame, followed by
   any extra positional arguments needed to create the key.
   
   
   Class Registries (NEW in version 0.6)
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   
   For many of common class role use cases, you just want a dictionary-like object
   with "inheritance" for the values in base classes.  The ``Registry`` base class
   provides this behavior, by subclassing ``ClassRole`` and the Python ``dict``
   builtin type, to create a class role that's also a dictionary.  It then
   overrides the ``created_for()`` method to automatically populate itself with
   any inherited values from base classes.
   
   Let's define a ``MethodGoodness`` registry that will store a "goodness"
   rating for methods::
   
       >>> from peak.util.roles import Registry
   
       >>> class MethodGoodness(Registry):
       ...     """Dictionary of method goodness"""
   
       >>> def goodness(value):
       ...     def decorate(func):
       ...         MethodGoodness.for_enclosing_class()[func.__name__]=value
       ...         return func
       ...     return decorate
   
       >>> class Demo(object):
       ...     def aMethod(self, foo):
       ...         pass
       ...     aMethod = goodness(17)(aMethod)
       ...     def another_method(whinge, spam):
       ...         woohoo
       ...     another_method = goodness(-99)(another_method)
   
       >>> MethodGoodness(Demo)
       {'aMethod': 17, 'another_method': -99}
   
   So far, so good.  Let's see what happens with a subclass::
   
       >>> class Demo2(Demo):
       ...     def another_method(self, fixed):
       ...         pass
       ...     another_method = goodness(42)(another_method)
   
       >>> MethodGoodness(Demo2)
       {'another_method': 42, 'aMethod': 17}
   
   Values set in base class registries are automatically added to the current
   class' registry of the same type and key, if the current class doesn't have
   an entry defined.  Python's new-style method resolution order is used to
   determine the precedence of inherited attributes.  (For classic classes, a
   temporary new-style class is created that inherits from the classic class, in
   order to determine the resolution order, then discarded.)
   
   Once the class in question has been created, the registry gets an extra
   attribute, ``defined_in_class``, which is a dictionary listing the entries that
   were actually defined in the corresponding class, e.g.::
   
       >>> MethodGoodness(Demo).defined_in_class
       {'aMethod': 17, 'another_method': -99}
   
       >>> MethodGoodness(Demo2).defined_in_class
       {'another_method': 42}
   
   As you can see, this second dictionary contains only the values registered in
   that class, and not any inherited values.
   
   Finally, note that ``Registry`` objects have one additional method that can
   be useful to call from a decorator: ``set(key, value)``.  This method will
   raise an error if a different value already exists for the given key, and is
   useful for catching errors in class definitions, e.g.:
   
       >>> def goodness(value):
       ...     def decorate(func):
       ...         MethodGoodness.for_enclosing_class().set(func.__name__, value)
       ...         return func
       ...     return decorate
   
       >>> class Demo3(object):
       ...     def aMethod(self, foo):
       ...         pass
       ...     aMethod = goodness(17)(aMethod)
       ...     def aMethod(self, foo):
       ...         pass
       ...     aMethod = goodness(27)(aMethod)
       Traceback (most recent call last):
         ...
       ValueError: MethodGoodness['aMethod'] already contains 17; can't set to 27
   
   
 Threading Concerns  Threading Concerns


Generate output suitable for use with a patch program
Legend:
Removed from v.2344  
changed lines
  Added in v.2353

cvs-admin@eby-sarna.com

Powered by ViewCVS 1.0-dev

ViewCVS and CVS Help