[CalendarServer-changes] [9658] CalendarServer/branches/users/glyph/q
source_changes at macosforge.org
source_changes at macosforge.org
Sat Aug 11 01:55:29 PDT 2012
Revision: 9658
http://trac.macosforge.org/projects/calendarserver/changeset/9658
Author: glyph at apple.com
Date: 2012-08-11 01:55:29 -0700 (Sat, 11 Aug 2012)
Log Message:
-----------
factor out naming convention, table specification, for increased friendliness to inspection of the code and IDEs
Modified Paths:
--------------
CalendarServer/branches/users/glyph/q/twext/enterprise/dal/record.py
CalendarServer/branches/users/glyph/q/twext/enterprise/dal/test/test_record.py
Property Changed:
----------------
CalendarServer/branches/users/glyph/q/
Modified: CalendarServer/branches/users/glyph/q/twext/enterprise/dal/record.py
===================================================================
--- CalendarServer/branches/users/glyph/q/twext/enterprise/dal/record.py 2012-08-11 08:55:28 UTC (rev 9657)
+++ CalendarServer/branches/users/glyph/q/twext/enterprise/dal/record.py 2012-08-11 08:55:29 UTC (rev 9658)
@@ -31,7 +31,7 @@
class ReadOnly(AttributeError):
"""
A caller attempted to set an attribute on a database-backed record, rather
- than updating it through L{_RecordBase.update}.
+ than updating it through L{Record.update}.
"""
def __init__(self, className, attributeName):
@@ -50,13 +50,72 @@
"""
+class _RecordMeta(type):
+ """
+ Metaclass for associating a L{fromTable} with a L{Record} at inheritance
+ time.
+ """
-class _RecordBase(object):
+ def __new__(cls, name, bases, ns):
+ """
+ Create a new instance of this meta-type.
+ """
+ newbases = []
+ table = None
+ namer = None
+ for base in bases:
+ if isinstance(base, fromTable):
+ if table is not None:
+ raise RuntimeError(
+ "Can't define a class from two or more tables at once."
+ )
+ table = base.table
+ elif getattr(base, "__tbl__", None) is not None:
+ raise RuntimeError(
+ "Can't define a record class by inheriting one already "
+ "mapped to a table."
+ # TODO: more info
+ )
+ else:
+ if namer is None:
+ if isinstance(base, _RecordMeta):
+ namer = base
+ newbases.append(base)
+ if table is not None:
+ attrmap = {}
+ colmap = {}
+ allColumns = list(table)
+ for column in allColumns:
+ attrname = namer.namingConvention(column.model.name)
+ attrmap[attrname] = column
+ colmap[column] = attrname
+ ns.update(__tbl__=table, __attrmap__=attrmap, __colmap__=colmap)
+ ns.update(attrmap)
+ return super(_RecordMeta, cls).__new__(cls, name, tuple(newbases), ns)
+
+
+
+class fromTable(object):
"""
+ Inherit from this after L{Record} to specify which table your L{Record}
+ subclass is mapped to.
+ """
+
+ def __init__(self, aTable):
+ """
+ @param table: The table to map to.
+ @type table: L{twext.enterprise.dal.syntax.TableSyntax}
+ """
+ self.table = aTable
+
+
+
+class Record(object):
+ """
Superclass for all database-backed record classes. (i.e. an object mapped
from a database record).
- @cvar __tbl__: the table that represents this L{_RecordBase} in the
+ @cvar __tbl__: the table that represents this L{Record} in the
database.
@type __tbl__: L{TableSyntax}
@@ -67,17 +126,29 @@
@type __attrmap__: L{dict}
"""
+ __metaclass__ = _RecordMeta
+
__txn__ = None
def __setattr__(self, name, value):
"""
Once the transaction is initialized, this object is immutable. If you
- want to change it, use L{_RecordBase.update}.
+ want to change it, use L{Record.update}.
"""
if self.__txn__ is not None:
raise ReadOnly(self.__class__.__name__, name)
- return super(_RecordBase, self).__setattr__(name, value)
+ return super(Record, self).__setattr__(name, value)
+ @staticmethod
+ def namingConvention(columnName):
+ """
+ Implement the convention for naming-conversion between column names
+ (typically, upper-case database names map to lower-case attribute
+ names).
+ """
+ return columnName.lower()
+
+
@classmethod
def _primaryKeyExpression(cls):
return Tuple([ColumnSyntax(c) for c in cls.__tbl__.model.primaryKey])
@@ -157,7 +228,7 @@
@classmethod
def pop(cls, txn, *primaryKey):
"""
- Atomically retrieve and remove a row from this L{_RecordBase}'s table
+ Atomically retrieve and remove a row from this L{Record}'s table
with a primary key value of C{primaryKey}.
@return: a L{Deferred} that fires with an instance of C{cls}, or fails
@@ -226,29 +297,6 @@
-def fromTable(table):
- """
- Create a L{type} that maps the columns from a particular table.
-
- A L{type} created in this manner will have instances with attributes that
- are mapped according to a naming convention like 'FOO_BAR' => 'fooBar'.
-
- @param table: The table.
- @type table: L{twext.enterprise.dal.syntax.TableSyntax}
- """
- attrmap = {}
- colmap = {}
- allColumns = list(table)
- for column in allColumns:
- attrname = column.model.name.lower()
- attrmap[attrname] = column
- colmap[column] = attrname
- ns = dict(__tbl__=table, __attrmap__=attrmap, __colmap__=colmap)
- ns.update(attrmap)
- return type(table.model.name, tuple([_RecordBase]), ns)
-
-
-
__all__ = [
"ReadOnly",
"fromTable",
Modified: CalendarServer/branches/users/glyph/q/twext/enterprise/dal/test/test_record.py
===================================================================
--- CalendarServer/branches/users/glyph/q/twext/enterprise/dal/test/test_record.py 2012-08-11 08:55:28 UTC (rev 9657)
+++ CalendarServer/branches/users/glyph/q/twext/enterprise/dal/test/test_record.py 2012-08-11 08:55:29 UTC (rev 9658)
@@ -24,7 +24,9 @@
from twisted.trial.unittest import TestCase
-from twext.enterprise.dal.record import fromTable, ReadOnly, NoSuchRecord
+from twext.enterprise.dal.record import (
+ Record, fromTable, ReadOnly, NoSuchRecord
+)
from twext.enterprise.dal.syntax import SQLITE_DIALECT
from twext.enterprise.dal.test.test_parseschema import SchemaTestHelper
@@ -44,7 +46,7 @@
-class TestRecord(fromTable(testSchema.ALPHA)):
+class TestRecord(Record, fromTable(testSchema.ALPHA)):
"""
A sample test record.
"""
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20120811/739d4c7a/attachment-0001.html>
More information about the calendarserver-changes
mailing list