Refactoring module mechanism

Changed the data structure $tw.modules.types to make it possible to
detect repeated registrations
This commit is contained in:
Jeremy Ruston 2012-11-14 11:23:43 +00:00
parent ce47f5e1d4
commit 539b64b626
8 changed files with 103 additions and 105 deletions

View File

@ -2,11 +2,9 @@
title: $:/core/boot.js
type: application/javascript
The main boot kernel for TiddlyWiki. This single file creates a barebones TW environment that is just
sufficient to bootstrap the modules containing the main logic of the application.
The main boot kernel for TiddlyWiki. This single file creates a barebones TW environment that is just sufficient to bootstrap the modules containing the main logic of the application.
On the server this file is executed directly to boot TiddlyWiki. In the browser, this file is packed
into a single HTML file along with other elements:
On the server this file is executed directly to boot TiddlyWiki. In the browser, this file is packed into a single HTML file along with other elements:
# bootprefix.js
# <module definitions>
@ -83,7 +81,7 @@ $tw.plugins = {};
// Modules store registers all the modules the system has seen
$tw.modules = $tw.modules || {};
$tw.modules.titles = $tw.modules.titles || {}; // hashmap by module title of {fn:, exports:, moduleType:}
$tw.modules.types = $tw.modules.types || {}; // hashmap by module type of array of exports
$tw.modules.types = $tw.modules.types || {}; // hashmap by module type of hashmap of exports
// Config object
$tw.config = $tw.config || {};
@ -300,9 +298,28 @@ Register the exports of a single module in the $tw.modules.types hashmap
*/
$tw.modules.registerModuleExports = function(name,moduleType,moduleExports) {
if(!$tw.utils.hop($tw.modules.types,moduleType)) {
$tw.modules.types[moduleType] = [];
$tw.modules.types[moduleType] = {};
}
if($tw.utils.hop($tw.modules.types[moduleType],name)) {
console.log("Warning: Reregistering module - " + name);
}
$tw.modules.types[moduleType][name] = moduleExports;
};
/*
Apply a callback to each module of a particular type
moduleType: type of modules to enumerate
callback: function called as callback(title,moduleExports) for each module
*/
$tw.modules.forEachModuleOfType = function(moduleType,callback) {
var modules = $tw.modules.types[moduleType];
if(modules) {
for(var title in modules) {
if($tw.utils.hop(modules,title)) {
callback(title,modules[title]);
}
}
}
$tw.modules.types[moduleType].push(moduleExports);
};
/*
@ -310,13 +327,10 @@ Get all the modules of a particular type in a hashmap by their `name` field
*/
$tw.modules.getModulesByTypeAsHashmap = function(moduleType,nameField) {
nameField = nameField || "name";
var modules = $tw.modules.types[moduleType],
results = {};
if(modules) {
for(var t=0; t<modules.length; t++) {
results[modules[t][nameField]] = modules[t];
}
}
var results = {};
$tw.modules.forEachModuleOfType(moduleType,function(title,module) {
results[module[nameField]] = module;
});
return results;
};
@ -324,16 +338,13 @@ $tw.modules.getModulesByTypeAsHashmap = function(moduleType,nameField) {
Apply the exports of the modules of a particular type to a target object
*/
$tw.modules.applyMethods = function(moduleType,object) {
var modules = $tw.modules.types[moduleType],
n,m,f;
if(modules) {
for(n=0; n<modules.length; n++) {
m = modules[n];
for(f in m) {
object[f] = m[f];
$tw.modules.forEachModuleOfType(moduleType,function(title,module) {
for(var field in module) {
if($tw.utils.hop(module,field)) {
object[field] = module[field];
}
}
}
});
};
/////////////////////////// Barebones tiddler object
@ -775,7 +786,7 @@ Load the tiddlers from a bundle folder, and package them up into a proper JSON b
*/
$tw.loadBundleFolder = function(filepath,excludeRegExp) {
excludeRegExp = excludeRegExp || /^\.DS_Store$|.meta$/;
var stat, files, bundleInfo = {tiddlers: []}, bundleTiddlers = [], f, file, titlePrefix, t;
var stat, files, bundleInfo, bundleTiddlers = [], f, file, titlePrefix, t;
if(fs.existsSync(filepath)) {
stat = fs.statSync(filepath);
if(stat.isDirectory()) {
@ -803,12 +814,12 @@ $tw.loadBundleFolder = function(filepath,excludeRegExp) {
}
}
// Save the bundle tiddler
return {
return bundleInfo ? {
title: bundleInfo.title,
type: "application/json",
bundle: "yes",
text: JSON.stringify(bundleInfo)
};
} : null;
};
/*
@ -879,20 +890,33 @@ if($tw.browser) {
// On the server, we load tiddlers from specified folders
var folders = [
$tw.boot.bootPath,
path.resolve($tw.boot.wikiPath,$tw.config.wikiPluginsSubDir),
path.resolve($tw.boot.wikiPath,$tw.config.wikiShadowsSubDir),
path.resolve($tw.boot.wikiPath,$tw.config.wikiTiddlersSubDir)
];
for(t=0; t<folders.length; t++) {
$tw.wiki.addTiddlers($tw.loadTiddlersFromPath(folders[t]));
}
// Load any plugins specified within the wiki folder
// Load any plugins listed in the wiki info file
var wikiInfoPath = path.resolve($tw.boot.wikiPath,$tw.config.wikiInfo);
if(fs.existsSync(wikiInfoPath)) {
var wikiInfo = JSON.parse(fs.readFileSync(wikiInfoPath,"utf8")),
pluginBasePath = path.resolve($tw.boot.bootPath,$tw.config.pluginsPath);
for(t=0; t<wikiInfo.plugins.length; t++) {
$tw.wiki.addTiddler($tw.loadBundleFolder(path.resolve(pluginBasePath,"./" + wikiInfo.plugins[t])));
var bundle = $tw.loadBundleFolder(path.resolve(pluginBasePath,"./" + wikiInfo.plugins[t]));
if(bundle) {
$tw.wiki.addTiddler(bundle);
}
}
}
// Load any plugins within the wiki folder
var wikiPluginsPath = path.resolve($tw.boot.wikiPath,$tw.config.wikiPluginsSubDir);
if(fs.existsSync(wikiPluginsPath)) {
var pluginFolders = fs.readdirSync(wikiPluginsPath);
for(t=0; t<pluginFolders.length; t++) {
var bundle = $tw.loadBundleFolder(path.resolve(wikiPluginsPath,"./" + pluginFolders[t]));
if(bundle) {
$tw.wiki.addTiddler(bundle);
}
}
}
}
@ -904,9 +928,10 @@ $tw.wiki.unpackBundleTiddlers();
$tw.wiki.registerModuleTiddlers();
// Run any startup modules
var mainModules = $tw.modules.types.startup;
for(var m=0; m<mainModules.length; m++) {
mainModules[m].startup();
}
$tw.modules.forEachModuleOfType("startup",function(title,module) {
if(module.startup) {
module.startup();
}
});
})();

View File

@ -26,6 +26,9 @@ Define a JavaScript tiddler module for later execution
fn: function defining the module, called with the arguments (module,require,exports)
*/
$tw.modules.define = function(moduleName,moduleType,fn) {
if(Object.prototype.hasOwnProperty.call($tw.modules.titles,moduleName)) {
console.log("Warning: Redefined module - " + moduleName);
}
$tw.modules.titles[moduleName] = {
moduleType: moduleType,
fn: fn

View File

@ -93,23 +93,17 @@ Commander.prototype.executeNextCommand = function() {
};
Commander.initCommands = function(moduleType) {
// Install the command modules
moduleType = moduleType || "command";
$tw.commands = {};
var modules = $tw.modules.types[moduleType],
n,m,f,c;
if(modules) {
for(n=0; n<modules.length; n++) {
m = modules[n];
$tw.commands[m.info.name] = {};
c = $tw.commands[m.info.name];
// Add the methods defined by the module
for(f in m) {
c[f] = m[f];
$tw.modules.forEachModuleOfType(moduleType,function(title,module) {
var c = $tw.commands[module.info.name] = {};
// Add the methods defined by the module
for(var f in module) {
if($tw.utils.hop(module,f)) {
c[f] = module[f];
}
}
}
});
};
exports.Commander = Commander;

View File

@ -266,17 +266,17 @@ var WikiTextParser = function(options) {
this.runRules = [];
var blockRegExpStrings = [],
runRegExpStrings = [],
rules = ($tw.modules.types.wikitextrule || []).slice(0);
for(var t=0; t<rules.length; t++) {
if(rules[t].blockParser) {
this.blockRules.push(rules[t]);
blockRegExpStrings.push("(" + rules[t].regExpString + ")");
self = this;
$tw.modules.forEachModuleOfType("wikitextrule",function(title,module) {
if(module.blockParser) {
self.blockRules.push(module);
blockRegExpStrings.push("(" + module.regExpString + ")");
}
if(rules[t].runParser) {
this.runRules.push(rules[t]);
runRegExpStrings.push("(" + rules[t].regExpString + ")");
if(module.runParser) {
self.runRules.push(module);
runRegExpStrings.push("(" + module.regExpString + ")");
}
}
});
this.blockRegExp = new RegExp(blockRegExpStrings.join("|"),"mg");
this.runRegExp = new RegExp(runRegExpStrings.join("|"),"mg");
};

View File

@ -42,12 +42,11 @@ exports.startup = function() {
// Host-specific startup
if($tw.browser) {
// Call browser startup modules
modules = $tw.modules.types["browser-startup"];
if(modules) {
for(m=0; m<modules.length; m++) {
modules[m].startup();
$tw.modules.forEachModuleOfType("browser-startup",function(title,module) {
if(module.startup) {
module.startup();
}
}
});
// Install the popup manager
$tw.popup = new $tw.utils.Popup({
wiki: $tw.wiki,
@ -113,7 +112,6 @@ exports.startup = function() {
{output: process.stdout, error: process.stderr}
);
commander.execute();
}
};

View File

@ -387,21 +387,18 @@ exports.clearCache = function(title) {
exports.initParsers = function(moduleType) {
// Install the parser modules
moduleType = moduleType || "parser";
$tw.wiki.parsers = {};
var modules = $tw.modules.types[moduleType],
n,m,f;
if(modules) {
for(n=0; n<modules.length; n++) {
m = modules[n];
// Add the parsers defined by the module
for(f in m) {
$tw.wiki.parsers[f] = new m[f]({wiki: this}); // Store an instance of the parser
$tw.wiki.parsers = {};
var self = this;
$tw.modules.forEachModuleOfType(moduleType,function(title,module) {
for(var f in module) {
if($tw.utils.hop(module,f)) {
$tw.wiki.parsers[f] = new module[f]({wiki: self}); // Store an instance of the parser
}
}
}
});
// Install the rules for the old wikitext parser rules
var wikitextparser = this.parsers["text/x-tiddlywiki-old"];
if(modules && wikitextparser) {
if(wikitextparser) {
wikitextparser.installRules();
}
};
@ -501,10 +498,8 @@ It's useful to remember what the `new` keyword does. It:
*/
exports.initMacros = function(moduleType) {
moduleType = moduleType || "macro";
$tw.wiki.macros = {};
$tw.wiki.macros = {};
var MacroClass = require("./treenodes/macro.js").Macro,
modules = $tw.modules.types[moduleType],
n,m,f,
subclassMacro = function(module) {
// Make a copy of the Macro() constructor function
var MacroMaker = function Macro() {
@ -514,17 +509,16 @@ exports.initMacros = function(moduleType) {
MacroMaker.prototype = new MacroClass();
// Add the prototype methods for this instance of the macro
for(var f in module) {
MacroMaker.prototype[f] = module[f];
if($tw.utils.hop(module,f)) {
MacroMaker.prototype[f] = module[f];
}
}
// Make a more convenient reference to the macro info
return MacroMaker;
};
if(modules) {
for(n=0; n<modules.length; n++) {
m = modules[n];
$tw.wiki.macros[m.info.name] = subclassMacro(m);
}
}
$tw.modules.forEachModuleOfType(moduleType,function(title,module) {
$tw.wiki.macros[module.info.name] = subclassMacro(module);
});
};
/*
@ -582,12 +576,12 @@ exports.initSavers = function(moduleType) {
moduleType = moduleType || "saver";
// Instantiate the available savers
this.savers = [];
for(var t=0; t<$tw.modules.types[moduleType].length; t++) {
var saver = $tw.modules.types[moduleType][t];
if(saver.canSave(this)) {
this.savers.push(saver.create(this));
var self = this;
$tw.modules.forEachModuleOfType(moduleType,function(title,module) {
if(module.canSave(self)) {
self.savers.push(module.create(self));
}
}
});
// Sort the savers into priority order
this.savers.sort(function(a,b) {
if(a.info.priority < b.info.priority) {

View File

@ -88,10 +88,9 @@ $tw.plugins.dropbox.login = function() {
Invoke any dropbox-startup modules
*/
$tw.plugins.dropbox.invokeDropboxStartupModules = function(loggedIn) {
var mods = $tw.modules.types["dropbox-startup"];
for(var m=0; m<mods.length; m++) {
mods[m].startup(loggedIn);
}
$tw.modules.forEachModuleOfType("dropbox-startup",function(title,module) {
module.startup(loggedIn);
});
};
/*

View File

@ -1,15 +0,0 @@
/*\
title: $:/tiddlywiki/plugins/demoplugin/demoplugin.js
type: application/javascript
module-type: demo
A demo plugin
\*/
(function(){
/*jslint node: true, browser: true */
"use strict";
})();