Monday, November 08, 2010 at 12:02 AM.
system.verbs.builtins.file.synchTableWithFolder
on synchTableWithFolder (folder, adrtable, adrcallback=nil) { <<Changes <<2/11/02; 2:17:35 AM by JES <<Fix for MacOS X users: Skip all files and folders whose names begin with a "." character, before doing any mod-date checking. This test was being done before, but too late for it to have worked properly. <<2/4/02; 9:55:40 AM by DW <<Fix for Mac OS X users. Ignore all files whose names begin with a dot. <<1/30/02; 7:54:08 PM by DW <<Earlier versions couldn't work with scalars because timeModified verb doesn't work with scalars. The problem was that the type "code" is a scalar type, and that's what we were working with. So the contents of the Macros folder was loaded every ten seconds whether or not there were any changes. <<We now keep a cache at system.temp.fileSynchCache, with an entry for each scalar we're managing, indexed by its file path, with a value of the last time it was loaded. This way if the file changes, we reload it, but otherwise we have a reliable mod date for the scalar. <<1/5/02; 1:00:36 PM by DW <<Added callback, called when a new object is loaded, the callback can do type coercion if it wants something other than the default, or it can delete the object. <<1/1/02; 7:44:23 AM by DW <<Created. I don't know why we didn't do this verb sooner, but it's killer, and very fast, something you can call from a background thread script to keep an object database table synchronized with a folder. We need this in work we're doing with Radio, where there's a new CMS where the content lives in the file system, now we can have renderers, macros, and perhaps other things in file system as well. <<http://docserver.userland.com/file/synchTableWithFolder bundle { //pass 1, add new or changed files local (adrcache = @system.temp.fileSynchCache); if not defined (adrcache^) { new (tabletype, adrcache)}; on dofolder (folder, adrtable) { local (f, fname, adr); if not defined (adrtable^) { new (tabletype, adrtable)}; fileloop (f in folder) { fname = file.filefrompath (f); if system.environment.isCarbon { //2/11/02 JES: all files/folders whose names start with "." on MacOS X are invisible if fname beginswith "." { continue}}; if file.isfolder (f) { adr = @adrtable^.[string.mid (fname, 1, sizeof (fname) - 1)]; dofolder (folder + nameof (adr^) + file.getpathchar (), adr)} else { local (flread = true, flscalar = false, adrincache); adr = @adrtable^.[string.popsuffix (fname)]; try { local (modtime = timemodified (adr)); if typeof (modtime) == booleantype { //scalar flscalar = true; adrincache = @adrcache^.[f]; if defined (adrincache^) { modtime = adrincache^} else { modtime = date (0)}}; if file.modified (f) <= modtime { flread = false}}; if flread { on loadFatDoc (f, adr) { local (s = fatPages.readSourceFile (f), atts); if fatPages.getPageAtts (@s, @atts) { fatPages.unpackOdbObject (@atts, adr, flRunnable:false, flEdit:false)}}; try {delete (adr)}; //so there won't be problems assigning over a wptext with a string, for example case string.lower (string.nthfield (fname, ".", string.countfields (fname, "."))) { "ftop"; "ftwp"; "ftmb"; "fttb"; "ftsc" { loadFatDoc (f, adr)}; "opml" { op.xmltooutline (file.readwholefile (f), adr)}; "gif" { adr^ = binary (file.readwholefile (f)); setbinarytype (adr, 'gif ')}; "jpeg"; "jpg" { adr^ = binary (file.readwholefile (f)); setbinarytype (adr, 'JPEG')}} else { adr^ = string (file.readwholefile (f))}; if adrcallback != nil { try {adrcallback^ (adr, f)}}; if flscalar { adrincache^ = clock.now ()}}}}}; if not file.exists (folder) { scriptError ("Can't synch the folder with the table because the folder \"" + folder + "\" does not exist.")}; dofolder (folder, adrtable)}; bundle { //pass 2, delete files/folders that no longer exist on dotable (adrtable, folder) { local (i, adr); for i = sizeof (adrtable^) downto 1 { adr = @adrtable^ [i]; if typeof (adr^) == tabletype { local (subfolder = folder + nameof (adr^) + file.getpathchar ()); if file.exists (subfolder) { dotable (adr, subfolder)} else { delete (adr)}} else { local (f = folder + nameof (adr^)); local (fldelete = false); case typeof (adr^) { scriptType { if not file.exists (f + ".ftsc") { fldelete = true}}; outlineType { if (not file.exists (f + ".ftop")) and (not file.exists (f + ".opml")) { fldelete = true}}; wptextType { if not file.exists (f + ".ftwp") { fldelete = true}}; menubarType { if not file.exists (f + ".ftmb") { fldelete = true}}; binaryType { case getBinaryType (adr^) { 'JPEG' { if (not file.exists (f + ".jpg")) and (not file.exists (f + ".jpeg")) { fldelete = true}}; 'gif ' { if not file.exists (f + ".gif") { fldelete = true}}} else { fldelete = true}}} else { if not file.exists (f + ".txt") { fldelete = true}}; if fldelete { delete (adr)}}}}; dotable (adrtable, folder)}; return (true)} <<bundle //new test code <<file.synchTableWithFolder (frontier.pathstring + "Macros" + file.getPathChar (), @radio.data.website.["#tools"], nil) <<bundle //old test code <<local (folder = frontier.pathstring + "testSynchWithFolder" + file.getpathchar ()) <<local (adrtable = @scratchpad.testSynchWithFolder) <<bundle //set up the folder <<local (i) <<for i = 1 to 10 <<local (f = folder + states.nthstate (i) + ".txt") <<file.surefilepath (f) <<file.writewholefile (f, i) <<adrtable = <<local (startticks = clock.ticks ()) <<synchTableWithFolder (folder, adrtable) <<dialog.alert (clock.ticks () - startticks)
This listing is for code that runs in the OPML Editor environment. I created these listings because I wanted the search engines to index it, so that when I want to look up something in my codebase I don't have to use the much slower search functionality in my object database. Dave Winer.