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.