Author: ianb
Date: 2006-02-22 21:07:09 -0700 (Wed, 22 Feb 2006)
New Revision: 1619
Added:
MetaSQLObject/trunk/docs/index.txt
Removed:
MetaSQLObject/trunk/metasqlobject/classregistry.py
Log:
Removed old module, added new doc
Added: MetaSQLObject/trunk/docs/index.txt
===================================================================
--- MetaSQLObject/trunk/docs/index.txt 2006-02-21 06:33:37 UTC (rev 1618)
+++ MetaSQLObject/trunk/docs/index.txt 2006-02-23 04:07:09 UTC (rev 1619)
@@ -0,0 +1,136 @@
+SQLObject 2
+===========
+
+So, what is this?
+
+This is a project to factor out pieces of SQLObject, and make it more
+comfortable to use SQLObject in combination with ad hoc queries, to
+build on SQLObject, and generally to do interesting things.
+
+Package Decomposition
+---------------------
+
+SQLObject 2 breaks SQLObject into three parts (roughly):
+
+* Database infrastructure that is logic-free. This is stuff that
+ could be used anywhere, and can be used in whole or in part. This
+ is what makes up SQL-API.
+
+* Metaprogramming tricks. These are things that aren't specific to
+ SQLObject, but are just general bits of code. Things like events,
+ threadlocal configuration, metaclasses. This is in the
+ ``metasqlobject`` package. Things will float in and out of this
+ package to start.
+
+* SQLObject itself, in the ``sqlobject`` package. This is the
+ object-relational mapper you've come to know and love. You love it,
+ right? Sure you do.
+
+Compatibility
+-------------
+
+Right now (Feb 2006) SQLObject 2 is pre-alpha, and is very not
+compatible with SQLObject 0.X. The test-driven development of
+SQLObject 2 will be to use as many of SQLObject's existing tests as
+possible.
+
+The goal is that if you use SQLObject 2, much of what you do will work
+right away. You'll get a bunch of warnings about things that are
+deprecated, and you'll get some (helpful) messages about things that
+are just plain gone.
+
+If you are digging inside SQLObject, it is very possible that your
+stuff will just be broken. But if you are using fairly public APIs,
+then you should do okay.
+
+``InheritableSQLObject`` is not part of SQLObject 2 at all at this
+point. I definitely want to change that, hopefully sooner rather than
+later. It might be possible to make that same functionality a part of
+the core of SQLObject using some of the new extension techniques,
+without a separate subclass.
+
+Architecture Differences
+------------------------
+
+The package separation is a big part of the difference.
+
+In general SQLObject also tries to move more of the data and logic out
+of the core classes -- ``SQLObject`` itself is quite small (especially
+when you consider that several of the functions are mostly made up of
+argument checks). ``sqlmeta`` really does the bulk of the work, but
+tries to stay small as well. Descriptors are used to greater effect.
+
+The event system is not fully developed yet, but is going to be a core
+part of the system. SQLObject (not-2's) trunk also has an event
+system. In SQLObject 2 the event system is ad hoc, because that was
+just a lot easier to handle in terms of subclassing (previously
+SQLObject used PyDispatcher). The event system will be a core part of
+the way all joins will work, among other more advanced features.
+
+Compound keys are not yet (fully) implemented, but are a core part of
+the design.
+
+Bound parameters are the only kind of queries really supported (fully
+rendered SQL is supported for logging and debugging only).
+
+SQLObject 2 tries to make up as few names as possible -- preferably no
+names. In some cases this means moving functionality elsewhere, in
+some cases being a bit more explicit. For instance ``ForeignKey``
+will be deprecated, and this::
+
+ class Foo(SQLObject):
+ other = ForeignKey('Other')
+
+Is now better done this way::
+
+ class Foo(SQLObject):
+ other_id = IntCol()
+ other = Reference('Other', 'other_id')
+
+Maybe at some future point ``other_id`` can be determined
+automatically, but the basic idea of the column as a separate entity
+from the reference will remain. I'm already very happy with how this
+is working. ``ForeignKey`` now implies exactly those two columns you
+see there.
+
+Names will be moving to underscore-style (PEP 8 compliant), instead of
+mixed case. There's not that many anyway -- ``selectBy``,
+``orderBy``, ``objectID``.
+
+SQLObject will work with DB-API-like connections (actually using a
+thin wrapper from SQL-API, but that wrapper goes right over DB-API
+connections). Logging happens below SQLObject now.
+
+Caching will be carefully considered. I want SQLObject 2 to be much
+more multiprocess-friendly, and much more predictable in terms of
+performance. Right now there is no caching, which introduces some
+problems (for instance, changes to an instance may not effect another
+instance that represents the same row). Probably the result will be
+caching not wholely unlike SQLObject's current caching, except with
+stricter thread affinity -- objects will not be cached or shared
+between threads. But we'll start from zero and work up, with firmer
+promises about how caching works.
+
+Compound queries will be possible -- i.e., fetching objects from more
+than one class at a time, including left joins. Some of the code is
+already in place for this. Features like lazy fetching of columns
+should be much easier. Lazy updates and inserts should be possible
+(i.e., explicit saves).
+
+Column objects are more powerful and more in control of their own
+existance. Column overrides are more explicit. So what was::
+
+ class User(SQLObject):
+ password = StringCol()
+ def _set_password(self, value):
+ self._SO_set_password(value.encode('rot13'))
+
+Will now be::
+
+ class User(SQLObject):
+ class password(StringCol):
+ def set(self, obj, value, raw_set):
+ raw_set(obj, value.encode('rot13')
+
+Well, that's just a few things. More to come!
+
Property changes on: MetaSQLObject/trunk/docs/index.txt
___________________________________________________________________
Name: svn:keywords
+ LastChangedDate LastChangedRevision
Name: svn:eol-style
+ native
Deleted: MetaSQLObject/trunk/metasqlobject/classregistry.py
===================================================================
--- MetaSQLObject/trunk/metasqlobject/classregistry.py 2006-02-21 06:33:37 UTC (rev 1618)
+++ MetaSQLObject/trunk/metasqlobject/classregistry.py 2006-02-23 04:07:09 UTC (rev 1619)
@@ -1,135 +0,0 @@
-"""
-classresolver.py
- 2 February 2004, Ian Bicking <ia...@co...>
-
-Resolves strings to classes, and runs callbacks when referenced
-classes are created.
-
-Classes are referred to only by name, not by module. So that
-identically-named classes can coexist, classes are put into individual
-registries, which are keyed on strings (names). These registries are
-created on demand.
-
-Use like::
-
- >>> import classregistry
- >>> registry = classregistry.registry('MyModules')
- >>> def afterMyClassExists(cls):
- ... print 'Class finally exists:', cls
- >>> registry.addClassCallback('MyClass', afterMyClassExists)
- >>> class MyClass:
- ... pass
- >>> registry.addClass(MyClass)
- Class finally exists: MyClass
-
-"""
-
-class ClassRegistry(object):
- """
- We'll be dealing with classes that reference each other, so
- class C1 may reference C2 (in a join), while C2 references
- C1 right back. Since classes are created in an order, there
- will be a point when C1 exists but C2 doesn't. So we deal
- with classes by name, and after each class is created we
- try to fix up any references by replacing the names with
- actual classes.
-
- Here we keep a dictionaries of class names to classes -- note
- that the classes might be spread among different modules, so
- since we pile them together names need to be globally unique,
- to just module unique.
- Like needSet below, the container dictionary is keyed by the
- class registry.
- """
-
- def __init__(self, name):
- self.name = name
- self.classes = {}
- self.callbacks = {}
- self.genericCallbacks = []
-
- def addClassCallback(self, className, callback, *args, **kw):
- """
- Whenever a name is substituted for the class, you can register
- a callback that will be called when the needed class is
- created. If it's already been created, the callback will be
- called immediately.
- """
- if self.classes.has_key(className):
- callback(self.classes[className], *args, **kw)
- else:
- self.callbacks.setdefault(className, []).append((callback, args, kw))
-
- def addCallback(self, callback, *args, **kw):
- """
- This callback is called for all classes, not just specific
- ones (like addClassCallback).
- """
- self.genericCallbacks.append((callback, args, kw))
- for cls in self.classes.values():
- callback(cls, *args, **kw)
-
- def addClass(self, cls):
- """
- Everytime a class is created, we add it to the registry, so
- that other classes can find it by name. We also call any
- callbacks that are waiting for the class.
- """
- if cls.__name__ in self.classes:
- import sys
- other = self.classes[cls.__name__]
- raise ValueError(
- "class %s is already in the registry (other class is "
- "%r, from the module %s in %s; attempted new class is "
- "%r, from the module %s in %s)"
- % (cls.__name__,
- other, other.__module__,
- getattr(sys.modules.get(other.__module__),
- '__file__', '(unknown)'),
- cls, cls.__module__,
- getattr(sys.modules.get(cls.__module__),
- '__file__', '(unknown)')))
- self.classes[cls.__name__] = cls
- if self.callbacks.has_key(cls.__name__):
- for callback, args, kw in self.callbacks[cls.__name__]:
- callback(cls, *args, **kw)
- del self.callbacks[cls.__name__]
- for callback, args, kw in self.genericCallbacks:
- callback(cls, *args, **kw)
-
- def getClass(self, className):
- try:
- return self.classes[className]
- except KeyError:
- all = self.classes.keys()
- all.sort()
- raise KeyError(
- "No class %s found in the registry %s (these classes "
- "exist: %s)"
- % (className, self.name or '[default]', ', '.join(all)))
-
- def allClasses(self):
- return self.classes.values()
-
-class _MasterRegistry(object):
- """
- This singleton holds all the class registries. There can be
- multiple registries to hold different unrelated sets of classes
- that reside in the same process. These registries are named with
- strings, and are created on demand. The MasterRegistry module
- global holds the singleton.
- """
-
- def __init__(self):
- self.registries = {}
-
- def registry(self, item):
- if not self.registries.has_key(item):
- self.registries[item] = ClassRegistry(item)
- return self.registries[item]
-
-MasterRegistry = _MasterRegistry()
-registry = MasterRegistry.registry
-
-def findClass(name, class_registry=None):
- return registry(class_registry).getClass(name)
|