I am starting to get pretty serious about using Rhino as a general
purpose server-side language, and one of the main issues that I still
need to solve is package/namespace management. I strongly suspect that
someone has developed a solution to this already, but if not then I
will be trying to figure something out. The characteristics that will
be needed are as follows:
1. Code in a given file needs to enter the global namespace only when
explicitly specified (presumably it needs to occupy its own scope?)
2. A package/namespace needs to be able to export functions that can
be imported and invoked from other packages
3. Ideally, the namespaces would be organized hierarchically so that
one could import foo.bar.* and receive the entire contents of the
package and any packages deeper in the hierarchy
4. Some mapping to actual files on the disk is of course called for,
as well as a way to load them into the process
In short, I need equivalent functionality to the Perl/Python/Ruby
family of dynamic languages (as these are the languages I hope to
replace with Rhino). I have some idea of how I would go about writing
such a facility, but given that this need has likely been felt before
I thought I would solicit any existing solution (or at least advice
about how to do it).
Has anybody seen or written anything like this? It seems like it might
make a lot of sense for Rhino to offer functionality like this out of
the box.
Thanks,
Ben Reesman
The package system I'm using addresses your points #1, #2 and #4.
Package definition:
Package("package-name", function(pkg) {
// package code goes here
pkg.exported_property = 3.14;
});
Package import:
const [pkg1, pkg2] = Package.get("pkg1-name", "pkg2-name");
Package also has a pluggable loading strategy (which is rather dependent
on my current project) and I also maintain a dependency graph for
dynamic reloading.
If that meets your requirements I can try and extract it (at least the
part covering your points #1 and #2.)
Christophe
What you describe is absolutely what I'm looking for. If you can
provide code that would be wonderful, and if not I would certainly
appreciate any description you could give. This sounds like such a
common thing to need, I'm surprised that a standard for this has not
emerged yet. If you are willing to share your code, perhaps it could
become the basis for a library?
Many Thanks,
Ben Reesman
> On Jan 10, 12:58 pm, Christophe Grand <christo...@cgrand.net> wrote:
>> ben.rees...@gmail.com a écrit :
>>
>>> Hi,
>>> I am starting to get pretty serious about using Rhino as a general
>>> purpose server-side language, and one of the main issues that I still
>>> need to solve is package/namespace management. I strongly suspect that
>>> someone has developed a solution to this already, but if not then I
...
> What you describe is absolutely what I'm looking for. If you can
> provide code that would be wonderful, and if not I would certainly
> appreciate any description you could give. This sounds like such a
> common thing to need, I'm surprised that a standard for this has not
> emerged yet. If you are willing to share your code, perhaps it could
> become the basis for a library?
>
> Many Thanks,
> Ben Reesman
I'm all for making a library (or a libraries repository) for Rhino based
on a package system -- I thought about that when I created this package
and put it aside, waiting to have more time...
Meanwhile, here is the code. Two remarks:
* it's not thread-safe (a couple of calls to sync may solve this issue),
* it depends on :
Function.prototype.autovarargs = function() {
var f = this;
return function() {
if (arguments.length > 1) {
return Array.prototype.map.call(arguments, f, this);
} else {
return f.apply(this, arguments);
}
}
};
// A package system for javascript
// Christophe Grand, 2007
Package = (function() {
var caller = "";
var dependencies = {};
var packages = {};
var unitPkgName = undefined;
// returned package may be a stub -- in case of recursive dependencies
function importPackage(name) {
if (packages[name] === undefined) {
dependencies[name] = dependencies[name] || {};
Package.loader(name);
}
dependencies[name][caller] = true;
return packages[name];
}
var Package = function(name, def) {
if (unitPkgName && unitPkgName !== name) {
if (app.log) log("Trying to define package: '" + String(name) + "'
while package '" + String(unitPkgName) + "' expected.");
return;
}
if (app.log) log("Loading package: '" + String(name) + "'");
if (packages[name] === undefined) {
var pkg = {};
packages[name] = pkg;
dependencies[name] = dependencies[name] || {};
var ccaller = caller;
caller = name;
try {
def(pkg);
} finally {
caller = ccaller;
}
} else {
if (app.log) log("Package '" + String(name) + "' already loaded!");
}
}
// Unload specified packages and packages depending on them
Package.unload = function() {
var unloaded = [];
function unload(name) {
var pkg = packages[name];
if (pkg === undefined) {
if (name === "") {
needReboot = true;
}
return;
}
if (app.log) log("Unloading package: '" + String(name) + "'");
packages[name] = undefined;
unloaded.push(name);
var dependents = dependencies[name];
if (dependents !== undefined) {
dependencies[name] = undefined;
for(var dependent in dependents) {
unload(dependent);
}
}
if (Package.onUnload) Package.onUnload(name, pkg);
};
var unloadVarArgs = unload.autovarargs();
unloadVarArgs.apply(this, arguments);
return unloaded;
}
// Unload specified packages and packages depending on them
// and reload them all
Package.reload = function() {
const unloaded = Package.unload.apply(this, arguments);
unloaded.forEach(Package.get);
}
Package.unloadAll = function() {
for(var name in packages) {
try {
Package.unload(name);
} catch (e) {
log(e);
}
}
}
// Package.defineOnly executes f but only allow declaration of Package
"name".
// Package.defineOnly is called from loaders implementations
Package.defineOnly = function(name, f) {
var bak = unitPkgName;
unitPkgName = name;
try {
f();
} finally {
unitPkgName = bak;
}
};
Package.get = importPackage.autovarargs();
// loader is a function taking one arg (the package name)
Package.loader = undefined;
// onUnload is an optional callback taking two args (the package name
and the package)
Package.onUnload = undefined;
return Package;
}) ();