Contact: fumanchu@aminus.org

Log in as guest/dejavu to create tickets

I think I've seen this ORM somewhere before...

Changeset 16

Show
Ignore:
Timestamp:
10/12/04 23:26:49
Author:
fumanchu
Message:

Doc, docstring, and comment updates

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/__init__.py

    r15 r16  
    239239     
    240240    def indices(cls): 
     241        """cls.indices() -> tuple of names of indexed UnitProperties.""" 
    241242        product = [] 
    242243        for key in cls.properties(): 
     
    251252     
    252253    def properties(cls): 
     254        """cls.properties() -> list of UnitProperty names.""" 
    253255        return cls._properties.iterkeys() 
    254256    properties = classmethod(properties) 
    255257     
    256258    def property_type(cls, key): 
     259        """cls.property_type(key) -> type of the given UnitProperty.""" 
    257260        # Retrieving from the class gives us 
    258261        # the UnitProperty object, not its value. 
     
    261264     
    262265    def adjust(self, **values): 
     266        """adjust(**values) -> Set UnitProperties by key, value pairs.""" 
    263267        for key, val in values.iteritems(): 
    264268            setattr(self, key, val) 
    265269     
    266270    def repress(self): 
     271        """repress() -> Remove this Unit from memory (do not destroy).""" 
    267272        self.sandbox.repress(self) 
    268273     
    269274    def forget(self): 
     275        """forget() -> Destroy this Unit.""" 
    270276        self.sandbox.forget(self) 
    271277     
     
    301307# The default ID type is int. If you wish to use a different type for 
    302308# the ID's of a subclass of Unit, just overwrite ID, e.g.: 
     309#     ID = UnitProperty('ID', unicode, index=True) 
     310#       or 
    303311#     UnitSubclass.set_property('ID', unicode, index=True) 
    304 # You will probably also want to override Unit.sequencer in the class body. 
     312#       or even 
     313#     UnitSubclass.ID.type = unicode 
     314# You will probably also want to override Unit.sequencer for the class. 
    305315Unit.set_property(u'ID', int, index=True) 
    306316 
  • trunk/containers.py

    r15 r16  
    99and map. 
    1010 
    11 Find herein the containers: Balloon, Chain, Graph, and Index
     11Find herein the containers: Balloon, Graph, Index, and Prism
    1212 
    1313This work, including the source code, documentation 
  • trunk/doc/index.html

    r15 r16  
    4848        <ul> 
    4949        <li>The <tt>Expression</tt> class</li> 
     50        <li>Early binding</li> 
    5051        <li>External functions within Expressions</li> 
    5152        <li>Combining Expressions</li> 
    52         <li>Expressions using <tt>filter</tt> and <tt>comparison</tt></li> 
     53        <li>Using <tt>filter</tt> to form Expressions</li> 
     54        <li>Using <tt>comparison</tt> to form Expressions</li> 
    5355        <li>Exporting the <tt>logic</tt> module</li> 
    5456        </ul> 
    5557    </li> 
    56     <li>Unit Engines</li> 
     58    <li>Associations between Unit Classes</li> 
     59    <li>Unit Engines 
     60        <ul> 
     61        <li>Engine functions</li> 
     62        </ul> 
     63    </li> 
    5764    <li>Analysis Tools</li> 
    5865    <li>The Arena Object 
    5966        <ul> 
    60         <li>Unit Class roster</li> 
     67        <li>Loading Stores</li> 
     68        <li>Registering Unit Classes</li> 
     69        <li></li> 
    6170        </ul> 
    6271    </li> 
  • trunk/doc/intro.html

    r15 r16  
    2929mindsets, each Unit subclass corresponds to a table; instances of the class 
    3030correspond to the rows. Each subclass possesses a set of attributes known 
    31 as <tt>properties</tt>, which you can think of as columns in your database 
     31as "properties", which you can think of as columns in your database 
    3232table. These attributes are generally formed from a <tt>UnitProperty</tt> 
    3333descriptor. Any Unit data which needs to be persisted ought to be contained 
     
    157157</p> 
    158158 
     159<p>In particular, Dejavu was designed to make complex integration easier. 
     160Unlike most generic storage wrappers, Dejavu does not <i>require</i> you to 
     161have complete control of your back end. For example, consider Mission 
     162Control, the first application built on Dejavu. Mission Control required 
     163an ORM which transparently supported two very different backends. Half of 
     164the data was to be stored in an MS Access database (which is being migrated 
     165to SQL Server), over which the application developers had full control. 
     166But half of the data was stored in a third-party application, "The Raiser's 
     167Edge" (RE) from Blackbaud. RE provides read-only database access; all writes 
     168must go through their object-oriented API. Further, reading via that API was 
     169found to be too slow. Therefore, a custom Storage Manager (about 2500 lines 
     170of code) was developed, which searches for and loads objects via SQL, but 
     171writes Unit data via the REAPI. Dejavu allows the application logic to be 
     172completely ignorant of this complex mass of storage details. If Blackbaud 
     173closed its doors tomorrow, the solution could be quickly migrated to another 
     174data store; business downtime is reduced in the face of inevitable change.<p> 
    159175 
    160176<h3>Obtaining and Installing</h3> 
     
    164180installed in <tt>site-packages/dejavu</tt>.</p> 
    165181 
     182<p>Dejavu was built using Python 2.3.2. You should probably use 
     183at least 2.3; Dejavu depends upon the <tt>datetime</tt> module. 
     184Although Dejavu <i>supports</i> additional modules like 
     185<tt>fixedpoint</tt> and <tt>decimal</tt>, it does not <i>require</i> 
     186them.</p> 
     187 
     188<p>Dejavu uses bytecode hacks, and therefore requires CPython 
     189<a href='#cpython'>[2]</a>.</p> 
     190 
    166191<h3>Compared To Other Database Wrappers</h3> 
    167192<p>TBW</p> 
    168193 
    169 <br /> 
    170  
    171 <p> 
    172 <a name='fowler'>[1]</a> Fowler, 
     194<hr /> 
     195 
     196<p><a name='fowler'>[1]</a> Fowler, 
    173197<a href='http://www.martinfowler.com/eaaCatalog/identityMap.html'>Patterns 
    174198of Enterprise Application Architecture</a>.<br /> 
  • trunk/doc/modeling.html

    r15 r16  
    114114<tt>index</tt> value tells Storage Managers whether or not to index the 
    115115column. The <tt>type</tt> attribute limits property values to instances 
    116 of that type, or None. Finally, the <tt>hints</tt> dictionary provides 
    117 hints to Storage Managers to help optimize storage. A common use, for 
    118 example, is to inform Managers that would usually store unicode strings 
     116of that type (or <tt>None</tt>). Finally, the <tt>hints</tt> dictionary 
     117provides hints to Storage Managers to help optimize storage. A common use, 
     118for example, is to inform Managers that would usually store unicode strings 
    119119as strings of length 255, that a particular value should be a larger object; 
    120120this is done with a 'Size' mapping, such as <tt>hints = {u'Size': 0}</tt>, 
     
    160160Storage Manager.</p> 
    161161 
    162 <p>You can create Sandbox objects directly. They take a single argument, 
    163 the top-level <tt>Arena</tt> object. Arenas provide a convenience function, 
     162<p>You can create Sandbox objects directly. They take a single argument, the 
     163top-level <tt>Arena</tt> object. Arenas also provide a convenience function, 
    164164<tt>new_sandbox</tt>, which does this for you. The following lines are 
    165165equivalent: 
     
    171171the Sandbox class.</p> 
    172172 
    173 <h4>Memorizing</h4> 
     173<h4>Memorizing Units</h4> 
    174174<p>When you create a Unit instance, it exists in isolation. There is no 
    175175connection between that Unit and storage; your Unit will not be persisted, 
     
    303303a given class. In Dejavu, you filter the set according to the UnitProperty 
    304304attributes for each object. Naturally, there must be a way to express 
    305 the filter you intend. Dejavu actually provides three ways.</p> 
     305the filter you intend. Dejavu actually provides three ways: Expressions, 
     306<tt>filter</tt>, and <tt>comparison</tt>.</p> 
    306307 
    307308<h4>The <tt>Expression</tt> class</h4> 
     
    320321into an Expression must possess a single positional argument, which will 
    321322always be bound to a Unit instance. In the example above, it's named 'x', 
    322 but you can use any name you like. For complete Unit objects residing in 
    323 memory, this arrangement means that we can simply call Expression.func(Unit), 
    324 and receive a boolean value indicating whether our Unit "passes the test". 
    325 Attribute lookups on our 'x' object will apply to Unit Properties for that 
    326 Unit object. That is, <tt>x.Date</tt> becomes <tt>Unit.Date</tt>.</p> 
    327  
     323but you can use any name you like. Using lambdas as a base means that we 
     324can simply call Expression.func(Unit), and receive a boolean value 
     325indicating whether our Unit "passes the test". Attribute lookups on our 
     326'x' object will apply to Unit Properties for that Unit object. 
     327That is, <tt>x.Date</tt> becomes <tt>Unit.Date</tt>.</p> 
     328 
     329<h4>Early binding</h4> 
    328330<p>What is not obvious from the above code snippet is perhaps the <b>most 
    329331important aspect</b> of Expressions: any globals or cell references (from  
     
    433435instead, they will simply retrieve a larger set of objects from storage, 
    434436evaluate each one against the function you provide, and return those 
    435 Units which match your function.</p> 
     437Units which match your function. This isn't necessarily a bad thing; 
     438it provides the same functionality as if you wrote the test inline 
     439within your own code. By making that test a logic function, you allow 
     440it to be stored in Engine <i>rules</i> (see <u>Unit Engines</u>,  
     441below).</p> 
    436442 
    437443<h4>Combining Expressions</h4> 
    438 <p>Expressions are combinable; by using the "&" operator, the two condition 
    439 lists are combined with an adjoining logical "and". For example: 
     444<p>Expressions are combinable; by using the <tt>&</tt> operator, the two 
     445expressions are combined with an adjoining logical "and". For example: 
    440446<pre>>>> a = logic.Expression(lambda x: x.Size > 3) 
    441447>>> b = logic.Expression(lambda x: x.Size <= 15) 
     
    446452<tt>|</tt> operator combines the two Expressions with a logical 'or'.</p> 
    447453 
    448 <h4>Expressions using <tt>filter</tt> and <tt>comparison</tt></h4> 
     454<h4>Using <tt>filter</tt> to form Expressions</h4> 
    449455<p>The <tt>logic</tt> module also provides convenient methods to 
    450456create common types of Expression objects via the <tt>filter</tt> and 
     
    458464</p> 
    459465 
     466<h4>Using <tt>comparison</tt> to form Expressions</h4> 
    460467<p>The <tt>comparison(attr, cmp_op, criteria)</tt> function allows you to 
    461468form Expressions with dynamic operators. This can come in handy when you 
     
    507514 
    508515 
     516<h3>Associations between Unit Classes</h3> 
     517<p>Once you've put together some Unit classes, chances are you're going to 
     518want to associate them. Generally, this is accomplished by creating a 
     519property in the Unit_B class which stores IDs of Unit_A objects (which 
     520might be called <i>foreign keys</i> in a database context). 
     521<pre>class Archaeologist(Unit): 
     522    Height = UnitProperty('Height', float) 
     523 
     524class Biography(Unit): 
     525    ArchID = UnitProperty('ArchID', int)</pre> 
     526In this example, each <tt>Biography</tt> object will have an <tt>ArchID</tt> 
     527attribute, which will equal the <tt>ID</tt> of some <tt>Archaeologist</tt>. 
     528In Dejavu terms, we say that there is a <i>near class</i> (with a <i>near 
     529key</i>) and a <i>far class</i> (with a <i>far key</i>). Associations in 
     530Dejavu are not one-way, so it doesn't matter which class you choose for the 
     531"near" one and which for the "far" one.</p> 
     532 
     533<p>You could stop at this point in your design, and simply remember what 
     534these keys are and how they relate, and manipulate them accordingly. But 
     535Dejavu allows you to <i>register</i> these associations explicitly in your 
     536<tt>Arena</tt>: 
     537<pre>myArena.associate(Archaeologist, 'ID', Biography, 'ArchID')</pre> 
     538You pass in the near class, the near key, the far class, and the far key. 
     539</p> 
     540 
     541<p>What does an explicit association buy for you? First, the <tt>associate</tt> 
     542call adds an entry in the <tt>Arena.associations</tt> registry, so that 
     543smart consumer code (like Unit Engine Rules, below) can automatically 
     544follow association paths for you. Second, each Unit class has a private 
     545<tt>_associations</tt> attribute, a <tt>dict</tt>. Each Unit involved 
     546in the association gains an entry in that dict: the key is the far class 
     547itself (not the class name), and the value is a tuple of (far key, near key). 
     548Third, <tt>associate()</tt> can be used to register your Unit classes in 
     549the Arena's <tt>roster</tt>; you don't have to call <tt>register</tt> for 
     550either class if you call <tt>associate</tt> (see <u>The Arena Object</u>, 
     551below).</p> 
     552 
     553<p>In addition, each of the Unit classes will gain a new <i>synapse</i> 
     554method which simplifies looking up related instances of the other class. 
     555The new method for Unit_B will have the name of Unit_A, and vice-versa. 
     556In our example: 
     557<pre>>>> Archaeologist.Biography 
     558&lt;unbound method Archaeologist.synapses> 
     559>>> Eversley = Archaeologist(Height=(6.417)) 
     560>>> Eversley.Biography 
     561&lt;bound method Archaeologist.synapses of &lt;__main__.Archaeologist 
     562object at 0x011A1930>> 
     563>>> bios = Eversley.Biography() 
     564>>> bios 
     565&lt;listiterator object at 0x012150D0> 
     566>>> list(bios) 
     567[] 
     568</pre> 
     569We haven't created any Biographies, so there aren't any to be recalled, 
     570which is why we get an empty iterator at this point. At the other extreme 
     571(when you have hundreds of Biographies to filter), you can pass an optional 
     572<tt>Expression</tt> object to the synapse method. When you do, the list of 
     573associated Units will be filtered accordingly.</p> 
     574 
     575<p>Because the synapse method names are formed automatically, you need 
     576to take care not to use the names of Unit classes for your Unit properties. 
     577In our example, we used "ArchID" for the name of our "foreign key". 
     578If we had used "Archaeologist" instead, we would have had problems; 
     579when we associated the classes, the <i>property</i> named "Archaeologist" 
     580would have collided with the <i>synapse method</i> named "Archaeologist". 
     581Be careful when naming your properties, and plan for the future.</p> 
     582 
     583<p>Unlike some other ORM's, Dejavu doesn't cache far Units within 
     584the near Unit. Each time you call the synapse method, the data is recalled 
     585from your Sandbox. It is quite probable that those far Units are still 
     586sitting in memory in the Sandbox, but they're not going to persist in 
     587the near Unit itself in any way.</p> 
     588 
     589<p>Finally, some of you may want to override the default synapse methods. 
     590Feel free; <tt>Arena.associate</tt> takes two optional arguments, which 
     591should be callables that return the new function(s). See the source code 
     592of <tt>Arena</tt> and the private method <tt>dejavu._synapses_func</tt> 
     593for more information.</p> 
     594 
    509595<h3>Unit Engines</h3> 
     596<p></p> 
     597 
     598<h4>Engine Functions</h4> 
    510599<p></p> 
    511600 
     
    524613running instance of the program.</p> 
    525614 
    526 <h4>Unit Class roster</h4> 
    527 <p></p> 
    528  
    529 <br /> 
     615<h4>Loading Stores</h4> 
     616<p>You <b>may</b> manually set up Storage Managers by calling 
     617<tt>Arena().add_store(name, store, unitClasses)</tt>. But, you 
     618probably shouldn't. Instead, allow your deployers to decide for 
     619themselves which storage solution(s) to use. You can do this by calling 
     620<tt>load(filename)</tt>; pass it the filename of an INI-style file 
     621which your deployers can tweak without screwing up your Python code. 
     622The next chapter in this reference is completely devoted to educating 
     623deployers; point them to it or copy/modify it in your own release docs.</p> 
     624 
     625<h4>Registering Unit Classes</h4> 
     626<p>The <tt>Arena</tt> object maintains a registry of Unit classes called a 
     627<tt>roster</tt>. A roster is like a three-way map between Unit classes, 
     628their names, and their assigned StorageManagers. You shouldn't manipulate 
     629this structure on your own; instead, use the <tt>register</tt> method to 
     630register each Unit class.</p> 
     631 
     632<p>The <tt>Arena</tt> object also manages the associations between Unit 
     633classes in its <tt>associations</tt> attribute, which is a simple, 
     634unweighted, undirected graph. In general, you should call 
     635<tt>associate(cls, key, farClass, farKey)</tt> to add classes to this 
     636graph. The only other common operation is to call 
     637<tt>.associations.shortest_path(start, end)</tt>, to retrieve the 
     638chain of associations between two Unit classes.</p> 
     639 
     640<hr /> 
    530641 
    531642<p><a name='hettinger'>[1]</a> Python Cookbook, 
  • trunk/storage/sockets.py

    r15 r16  
    100100        data = dechunk(''.join(data)) 
    101101        if data and data[0] == 'ERROR': 
     102            # The value in data[1] is a pickled Exception. 
    102103            raise pickle.loads(data[1]) 
    103104        return data