Changeset 16
- Timestamp:
- 10/12/04 23:26:49
- Files:
-
- trunk/__init__.py (modified) (4 diffs)
- trunk/containers.py (modified) (1 diff)
- trunk/doc/index.html (modified) (1 diff)
- trunk/doc/intro.html (modified) (3 diffs)
- trunk/doc/modeling.html (modified) (10 diffs)
- trunk/storage/sockets.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/__init__.py
r15 r16 239 239 240 240 def indices(cls): 241 """cls.indices() -> tuple of names of indexed UnitProperties.""" 241 242 product = [] 242 243 for key in cls.properties(): … … 251 252 252 253 def properties(cls): 254 """cls.properties() -> list of UnitProperty names.""" 253 255 return cls._properties.iterkeys() 254 256 properties = classmethod(properties) 255 257 256 258 def property_type(cls, key): 259 """cls.property_type(key) -> type of the given UnitProperty.""" 257 260 # Retrieving from the class gives us 258 261 # the UnitProperty object, not its value. … … 261 264 262 265 def adjust(self, **values): 266 """adjust(**values) -> Set UnitProperties by key, value pairs.""" 263 267 for key, val in values.iteritems(): 264 268 setattr(self, key, val) 265 269 266 270 def repress(self): 271 """repress() -> Remove this Unit from memory (do not destroy).""" 267 272 self.sandbox.repress(self) 268 273 269 274 def forget(self): 275 """forget() -> Destroy this Unit.""" 270 276 self.sandbox.forget(self) 271 277 … … 301 307 # The default ID type is int. If you wish to use a different type for 302 308 # the ID's of a subclass of Unit, just overwrite ID, e.g.: 309 # ID = UnitProperty('ID', unicode, index=True) 310 # or 303 311 # 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. 305 315 Unit.set_property(u'ID', int, index=True) 306 316 trunk/containers.py
r15 r16 9 9 and map. 10 10 11 Find herein the containers: Balloon, Chain, Graph, and Index.11 Find herein the containers: Balloon, Graph, Index, and Prism. 12 12 13 13 This work, including the source code, documentation trunk/doc/index.html
r15 r16 48 48 <ul> 49 49 <li>The <tt>Expression</tt> class</li> 50 <li>Early binding</li> 50 51 <li>External functions within Expressions</li> 51 52 <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> 53 55 <li>Exporting the <tt>logic</tt> module</li> 54 56 </ul> 55 57 </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> 57 64 <li>Analysis Tools</li> 58 65 <li>The Arena Object 59 66 <ul> 60 <li>Unit Class roster</li> 67 <li>Loading Stores</li> 68 <li>Registering Unit Classes</li> 69 <li></li> 61 70 </ul> 62 71 </li> trunk/doc/intro.html
r15 r16 29 29 mindsets, each Unit subclass corresponds to a table; instances of the class 30 30 correspond 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 database31 as "properties", which you can think of as columns in your database 32 32 table. These attributes are generally formed from a <tt>UnitProperty</tt> 33 33 descriptor. Any Unit data which needs to be persisted ought to be contained … … 157 157 </p> 158 158 159 <p>In particular, Dejavu was designed to make complex integration easier. 160 Unlike most generic storage wrappers, Dejavu does not <i>require</i> you to 161 have complete control of your back end. For example, consider Mission 162 Control, the first application built on Dejavu. Mission Control required 163 an ORM which transparently supported two very different backends. Half of 164 the data was to be stored in an MS Access database (which is being migrated 165 to SQL Server), over which the application developers had full control. 166 But half of the data was stored in a third-party application, "The Raiser's 167 Edge" (RE) from Blackbaud. RE provides read-only database access; all writes 168 must go through their object-oriented API. Further, reading via that API was 169 found to be too slow. Therefore, a custom Storage Manager (about 2500 lines 170 of code) was developed, which searches for and loads objects via SQL, but 171 writes Unit data via the REAPI. Dejavu allows the application logic to be 172 completely ignorant of this complex mass of storage details. If Blackbaud 173 closed its doors tomorrow, the solution could be quickly migrated to another 174 data store; business downtime is reduced in the face of inevitable change.<p> 159 175 160 176 <h3>Obtaining and Installing</h3> … … 164 180 installed in <tt>site-packages/dejavu</tt>.</p> 165 181 182 <p>Dejavu was built using Python 2.3.2. You should probably use 183 at least 2.3; Dejavu depends upon the <tt>datetime</tt> module. 184 Although Dejavu <i>supports</i> additional modules like 185 <tt>fixedpoint</tt> and <tt>decimal</tt>, it does not <i>require</i> 186 them.</p> 187 188 <p>Dejavu uses bytecode hacks, and therefore requires CPython 189 <a href='#cpython'>[2]</a>.</p> 190 166 191 <h3>Compared To Other Database Wrappers</h3> 167 192 <p>TBW</p> 168 193 169 <br /> 170 171 <p> 172 <a name='fowler'>[1]</a> Fowler, 194 <hr /> 195 196 <p><a name='fowler'>[1]</a> Fowler, 173 197 <a href='http://www.martinfowler.com/eaaCatalog/identityMap.html'>Patterns 174 198 of Enterprise Application Architecture</a>.<br /> trunk/doc/modeling.html
r15 r16 114 114 <tt>index</tt> value tells Storage Managers whether or not to index the 115 115 column. The <tt>type</tt> attribute limits property values to instances 116 of that type , or None. Finally, the <tt>hints</tt> dictionary provides117 hints to Storage Managers to help optimize storage. A common use, for 118 example, is to inform Managers that would usually store unicode strings116 of that type (or <tt>None</tt>). Finally, the <tt>hints</tt> dictionary 117 provides hints to Storage Managers to help optimize storage. A common use, 118 for example, is to inform Managers that would usually store unicode strings 119 119 as strings of length 255, that a particular value should be a larger object; 120 120 this is done with a 'Size' mapping, such as <tt>hints = {u'Size': 0}</tt>, … … 160 160 Storage Manager.</p> 161 161 162 <p>You can create Sandbox objects directly. They take a single argument, 163 t he top-level <tt>Arena</tt> object. Arenasprovide a convenience function,162 <p>You can create Sandbox objects directly. They take a single argument, the 163 top-level <tt>Arena</tt> object. Arenas also provide a convenience function, 164 164 <tt>new_sandbox</tt>, which does this for you. The following lines are 165 165 equivalent: … … 171 171 the Sandbox class.</p> 172 172 173 <h4>Memorizing </h4>173 <h4>Memorizing Units</h4> 174 174 <p>When you create a Unit instance, it exists in isolation. There is no 175 175 connection between that Unit and storage; your Unit will not be persisted, … … 303 303 a given class. In Dejavu, you filter the set according to the UnitProperty 304 304 attributes for each object. Naturally, there must be a way to express 305 the filter you intend. Dejavu actually provides three ways.</p> 305 the filter you intend. Dejavu actually provides three ways: Expressions, 306 <tt>filter</tt>, and <tt>comparison</tt>.</p> 306 307 307 308 <h4>The <tt>Expression</tt> class</h4> … … 320 321 into an Expression must possess a single positional argument, which will 321 322 always 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 323 but you can use any name you like. Using lambdas as a base means that we 324 can simply call Expression.func(Unit), and receive a boolean value 325 indicating whether our Unit "passes the test". Attribute lookups on our 326 'x' object will apply to Unit Properties for that Unit object. 327 That is, <tt>x.Date</tt> becomes <tt>Unit.Date</tt>.</p> 328 329 <h4>Early binding</h4> 328 330 <p>What is not obvious from the above code snippet is perhaps the <b>most 329 331 important aspect</b> of Expressions: any globals or cell references (from … … 433 435 instead, they will simply retrieve a larger set of objects from storage, 434 436 evaluate each one against the function you provide, and return those 435 Units which match your function.</p> 437 Units which match your function. This isn't necessarily a bad thing; 438 it provides the same functionality as if you wrote the test inline 439 within your own code. By making that test a logic function, you allow 440 it to be stored in Engine <i>rules</i> (see <u>Unit Engines</u>, 441 below).</p> 436 442 437 443 <h4>Combining Expressions</h4> 438 <p>Expressions are combinable; by using the "&" operator, the two condition439 lists are combined with an adjoining logical "and". For example:444 <p>Expressions are combinable; by using the <tt>&</tt> operator, the two 445 expressions are combined with an adjoining logical "and". For example: 440 446 <pre>>>> a = logic.Expression(lambda x: x.Size > 3) 441 447 >>> b = logic.Expression(lambda x: x.Size <= 15) … … 446 452 <tt>|</tt> operator combines the two Expressions with a logical 'or'.</p> 447 453 448 <h4> Expressions using <tt>filter</tt> and <tt>comparison</tt></h4>454 <h4>Using <tt>filter</tt> to form Expressions</h4> 449 455 <p>The <tt>logic</tt> module also provides convenient methods to 450 456 create common types of Expression objects via the <tt>filter</tt> and … … 458 464 </p> 459 465 466 <h4>Using <tt>comparison</tt> to form Expressions</h4> 460 467 <p>The <tt>comparison(attr, cmp_op, criteria)</tt> function allows you to 461 468 form Expressions with dynamic operators. This can come in handy when you … … 507 514 508 515 516 <h3>Associations between Unit Classes</h3> 517 <p>Once you've put together some Unit classes, chances are you're going to 518 want to associate them. Generally, this is accomplished by creating a 519 property in the Unit_B class which stores IDs of Unit_A objects (which 520 might be called <i>foreign keys</i> in a database context). 521 <pre>class Archaeologist(Unit): 522 Height = UnitProperty('Height', float) 523 524 class Biography(Unit): 525 ArchID = UnitProperty('ArchID', int)</pre> 526 In this example, each <tt>Biography</tt> object will have an <tt>ArchID</tt> 527 attribute, which will equal the <tt>ID</tt> of some <tt>Archaeologist</tt>. 528 In Dejavu terms, we say that there is a <i>near class</i> (with a <i>near 529 key</i>) and a <i>far class</i> (with a <i>far key</i>). Associations in 530 Dejavu 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 534 these keys are and how they relate, and manipulate them accordingly. But 535 Dejavu allows you to <i>register</i> these associations explicitly in your 536 <tt>Arena</tt>: 537 <pre>myArena.associate(Archaeologist, 'ID', Biography, 'ArchID')</pre> 538 You 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> 542 call adds an entry in the <tt>Arena.associations</tt> registry, so that 543 smart consumer code (like Unit Engine Rules, below) can automatically 544 follow association paths for you. Second, each Unit class has a private 545 <tt>_associations</tt> attribute, a <tt>dict</tt>. Each Unit involved 546 in the association gains an entry in that dict: the key is the far class 547 itself (not the class name), and the value is a tuple of (far key, near key). 548 Third, <tt>associate()</tt> can be used to register your Unit classes in 549 the Arena's <tt>roster</tt>; you don't have to call <tt>register</tt> for 550 either class if you call <tt>associate</tt> (see <u>The Arena Object</u>, 551 below).</p> 552 553 <p>In addition, each of the Unit classes will gain a new <i>synapse</i> 554 method which simplifies looking up related instances of the other class. 555 The new method for Unit_B will have the name of Unit_A, and vice-versa. 556 In our example: 557 <pre>>>> Archaeologist.Biography 558 <unbound method Archaeologist.synapses> 559 >>> Eversley = Archaeologist(Height=(6.417)) 560 >>> Eversley.Biography 561 <bound method Archaeologist.synapses of <__main__.Archaeologist 562 object at 0x011A1930>> 563 >>> bios = Eversley.Biography() 564 >>> bios 565 <listiterator object at 0x012150D0> 566 >>> list(bios) 567 [] 568 </pre> 569 We haven't created any Biographies, so there aren't any to be recalled, 570 which 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 573 associated Units will be filtered accordingly.</p> 574 575 <p>Because the synapse method names are formed automatically, you need 576 to take care not to use the names of Unit classes for your Unit properties. 577 In our example, we used "ArchID" for the name of our "foreign key". 578 If we had used "Archaeologist" instead, we would have had problems; 579 when we associated the classes, the <i>property</i> named "Archaeologist" 580 would have collided with the <i>synapse method</i> named "Archaeologist". 581 Be 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 584 the near Unit. Each time you call the synapse method, the data is recalled 585 from your Sandbox. It is quite probable that those far Units are still 586 sitting in memory in the Sandbox, but they're not going to persist in 587 the near Unit itself in any way.</p> 588 589 <p>Finally, some of you may want to override the default synapse methods. 590 Feel free; <tt>Arena.associate</tt> takes two optional arguments, which 591 should be callables that return the new function(s). See the source code 592 of <tt>Arena</tt> and the private method <tt>dejavu._synapses_func</tt> 593 for more information.</p> 594 509 595 <h3>Unit Engines</h3> 596 <p></p> 597 598 <h4>Engine Functions</h4> 510 599 <p></p> 511 600 … … 524 613 running instance of the program.</p> 525 614 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 618 probably shouldn't. Instead, allow your deployers to decide for 619 themselves 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 621 which your deployers can tweak without screwing up your Python code. 622 The next chapter in this reference is completely devoted to educating 623 deployers; 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, 628 their names, and their assigned StorageManagers. You shouldn't manipulate 629 this structure on your own; instead, use the <tt>register</tt> method to 630 register each Unit class.</p> 631 632 <p>The <tt>Arena</tt> object also manages the associations between Unit 633 classes in its <tt>associations</tt> attribute, which is a simple, 634 unweighted, undirected graph. In general, you should call 635 <tt>associate(cls, key, farClass, farKey)</tt> to add classes to this 636 graph. The only other common operation is to call 637 <tt>.associations.shortest_path(start, end)</tt>, to retrieve the 638 chain of associations between two Unit classes.</p> 639 640 <hr /> 530 641 531 642 <p><a name='hettinger'>[1]</a> Python Cookbook, trunk/storage/sockets.py
r15 r16 100 100 data = dechunk(''.join(data)) 101 101 if data and data[0] == 'ERROR': 102 # The value in data[1] is a pickled Exception. 102 103 raise pickle.loads(data[1]) 103 104 return data
