Code

more metakit fixes
authorrichard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2>
Wed, 24 Mar 2004 05:33:13 +0000 (05:33 +0000)
committerrichard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2>
Wed, 24 Mar 2004 05:33:13 +0000 (05:33 +0000)
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@2171 57a73879-2fb5-44c3-a270-3262357dd7e2

roundup/backends/back_metakit.py
test/test_metakit.py

index 6e41516137b4e866eb1a3cb5b5e63b2ccdda7419..a10662596f3aae825316c94103a23b85ada35091 100755 (executable)
@@ -1,4 +1,4 @@
-# $Id: back_metakit.py,v 1.67 2004-03-22 07:45:39 richard Exp $
+# $Id: back_metakit.py,v 1.68 2004-03-24 05:33:13 richard Exp $
 '''Metakit backend for Roundup, originally by Gordon McMillan.
 
 Known Current Bugs:
@@ -48,6 +48,7 @@ import re, marshal, os, sys, time, calendar
 from indexer_dbm import Indexer
 import locking
 from roundup.date import Range
+from blobfiles import files_in_dir
 
 # view modes for opening
 # XXX FIXME BPK -> these don't do anything, they are ignored
@@ -324,6 +325,12 @@ class _Database(hyperdb.Database, roundupdb.Database):
         ''' No-op in metakit
         '''
         pass
+
+    def numfiles(self):
+        '''Get number of files in storage, even across subdirectories.
+        '''
+        files_dir = os.path.join(self.config.DATABASE, 'files')
+        return files_in_dir(files_dir)
         
 _STRINGTYPE = type('')
 _LISTTYPE = type([])
@@ -377,6 +384,7 @@ class Class(hyperdb.Class):
         if view:
             self.maxid = view[-1].id + 1
         self.uncommitted = {}
+        self.comactions = []
         self.rbactions = []
 
         # people reach inside!!
@@ -1533,11 +1541,15 @@ class Class(hyperdb.Class):
         ''' called post commit of the DB.
             interested subclasses may override '''
         self.uncommitted = {}
+        for action in self.comactions:
+            action()
+        self.comactions = []
         self.rbactions = []
         self.idcache = {}
     def _rollback(self):  
         ''' called pre rollback of the DB.
             interested subclasses may override '''
+        self.comactions = []
         for action in self.rbactions:
             action()
         self.rbactions = []
@@ -1551,6 +1563,10 @@ class Class(hyperdb.Class):
         iv = self.getindexview(READWRITE)
         if iv:
             iv[:] = []
+    def commitaction(self, action):
+        ''' call this to register a callback called on commit
+            callback is removed on end of transaction '''
+        self.comactions.append(action)
     def rollbackaction(self, action):
         ''' call this to register a callback called on rollback
             callback is removed on end of transaction '''
@@ -1732,6 +1748,8 @@ class FileClass(Class, hyperdb.FileClass):
         if propname == 'content':
             poss_msg = 'Possibly an access right configuration problem.'
             fnm = self.gen_filename(nodeid)
+            if not os.path.exists(fnm):
+                fnm = fnm + '.tmp'
             try:
                 f = open(fnm, 'rb')
             except IOError, (strerror):
@@ -1758,16 +1776,56 @@ class FileClass(Class, hyperdb.FileClass):
 
         # figure a filename
         nm = self.gen_filename(newid)
-        open(nm, 'wb').write(content)
+        f = open(nm + '.tmp', 'wb')
+        f.write(content)
+        f.close()
 
         mimetype = propvalues.get('type', self.default_mime_type)
         self.db.indexer.add_text((self.classname, newid, 'content'), content,
             mimetype)
+
+        # register commit and rollback actions
+        def commit(fnm=nm):
+            os.rename(fnm + '.tmp', fnm)
+        self.commitaction(commit)
         def undo(fnm=nm):
-            os.remove(fnm)
+            os.remove(fnm + '.tmp')
         self.rollbackaction(undo)
         return newid
 
+    def set(self, itemid, **propvalues):
+        if not propvalues:
+            return
+        self.fireAuditors('set', None, propvalues)
+
+        content = propvalues.get('content', None)
+        if content is not None:
+            del propvalues['content']
+
+        propvalues, oldnode = Class.set_inner(self, itemid, **propvalues)
+
+        # figure a filename
+        if content is not None:
+            nm = self.gen_filename(itemid)
+            f = open(nm + '.tmp', 'wb')
+            f.write(content)
+            f.close()
+            mimetype = propvalues.get('type', self.default_mime_type)
+            self.db.indexer.add_text((self.classname, itemid, 'content'),
+                content, mimetype)
+
+            # register commit and rollback actions
+            def commit(fnm=nm):
+                if os.path.exists(fnm):
+                    os.remove(fnm)
+                os.rename(fnm + '.tmp', fnm)
+            self.commitaction(commit)
+            def undo(fnm=nm):
+                os.remove(fnm + '.tmp')
+            self.rollbackaction(undo)
+
+        self.fireReactors('set', oldnode, propvalues)
+
     def index(self, nodeid):
         Class.index(self, nodeid)
         mimetype = self.get(nodeid, 'type')
index 845f4c291cc40172beda73008539c5e3e428d51c..f4d95d630bcc94e25151403e5f02fe5bd3421c9c 100644 (file)
@@ -15,7 +15,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: test_metakit.py,v 1.4 2004-03-18 01:58:46 richard Exp $ 
+# $Id: test_metakit.py,v 1.5 2004-03-24 05:33:13 richard Exp $ 
 import unittest, os, shutil, time, weakref
 
 from db_test_base import DBTest, ROTest, SchemaTest, ClassicInitTest, config, password
@@ -31,37 +31,6 @@ class metakitOpener:
         shutil.rmtree(config.DATABASE)
 
 class metakitDBTest(metakitOpener, DBTest):
-    def testTransactions(self):
-        # remember the number of items we started
-        num_issues = len(self.db.issue.list())
-        self.db.issue.create(title="don't commit me!", status='1')
-        self.assertNotEqual(num_issues, len(self.db.issue.list()))
-        self.db.rollback()
-        self.assertEqual(num_issues, len(self.db.issue.list()))
-        self.db.issue.create(title="please commit me!", status='1')
-        self.assertNotEqual(num_issues, len(self.db.issue.list()))
-        self.db.commit()
-        self.assertNotEqual(num_issues, len(self.db.issue.list()))
-        self.db.rollback()
-        self.assertNotEqual(num_issues, len(self.db.issue.list()))
-        self.db.file.create(name="test", type="text/plain", content="hi")
-        self.db.rollback()
-        num_files = len(self.db.file.list())
-        for i in range(10):
-            self.db.file.create(name="test", type="text/plain", 
-                    content="hi %d"%(i))
-            self.db.commit()
-        # TODO: would be good to be able to ensure the file is not on disk after
-        # a rollback...
-        num_files2 = len(self.db.file.list())
-        self.assertNotEqual(num_files, num_files2)
-        self.db.file.create(name="test", type="text/plain", content="hi")
-        num_rfiles = len(os.listdir(self.db.config.DATABASE + '/files/file/0'))
-        self.db.rollback()
-        num_rfiles2 = len(os.listdir(self.db.config.DATABASE + '/files/file/0'))
-        self.assertEqual(num_files2, len(self.db.file.list()))
-        self.assertEqual(num_rfiles2, num_rfiles-1)
-
     def testBooleanUnset(self):
         # XXX: metakit can't unset Booleans :(
         nid = self.db.user.create(username='foo', assignable=1)