Access Keys:
Skip to content (Access Key - 0)
Log in (Access Key - 5)



Toggle Sidebar

JSModules

Molybdenum has its own implementation of javascript modules in contrast to the Firefox 3 Components.utils.import.
The Molybdenum implementation works in Firefox 2 as well.

Goals

  • Move javascript file dependencies out of the XUL file into the javascript file itself
  • Allow javascript object to behave like singletons
  • Enforce a clean codestyle where a file is containing one function only

Implementation

The implementation is in an XPCom service with name @molyb.org/molybase;1.
The service has following methods:

  • inject(modulePath, scopeObject)
  • clearCache()

Usage

  1. Put your modules into the directory content/modules. Subdirectories are allowed. Modules must have the extension .mmod (Molybdenum Modules).
  2. The *.mmod file has to hold exactly one function with the same name as the file itself.
  3. Lookup the MolyBase service
  4. call inject

Singleton Example

test/TestSingleton.mmod
function TestSingleton() {};
TestSingleton.dump = function() {
  return "object Test";
}
injecting and using TestSingleton
var MolyBase = Components.classes['@molyb.org/molybase;1'].getService().wrappedJSObject;
try {
  MolyBase.inject("test/TestSingleton", this);    
  alert(this.TestSingleton.dump());
} catch(e) {
  alert(e.message);
}

The code above adds the loaded function as property to "this" (the object requiring the collaborator). This means, the object instance has already been created before the collaborator gets injected.

You can inject outside of the constructor function as well:

injecting singleton object alternative
// the next line is only needed if MyComp is not a module itself
MyComp.MolyBase = Components.classes['@molyb.org/molybase;1'].getService().wrappedJSObject;
MyComp.MolyBase.inject("test/TestSingleton", MyComp);

function MyComp() {
    alert( MyComp.TestSingleton.dump() );
}

Non-Singleton Example

When injecting prototypical objects, you need to instantiate the injected object.
Consider splitting the injection in the "load and import"- part and the instantiation part:

test/Test.mmod
function Test() {};
Test.prototype.dump = function() {
  return "object Test";
}
injecting prototypical object into MyComp
// the next line is only needed if MyComp is not a module itself
MyComp.MolyBase = Components.classes['@molyb.org/molybase;1'].getService().wrappedJSObject;
MyComp.MolyBase.inject("test/Test", MyComp);

MyComp.prototype.test = new MyComp.Test();

function MyComp() {
    alert( this.test.dump() );
}

Details

MolyBase.inject has to be called with the path of the module to be loaded and a scope object the function should be applied to.
The path must be provided without prefix content/modules and without extension .mmod.
The inject function will get the module from the cache or, if not found in the cache, loads the module via subscript loader.
After successful load, the function with the name of the module, the last component of the module path, will be assigned to a property of the scopeObject with the same name.
If something goes wrong, an exception is thrown.

Not shown in the example, clearCache can be called to clear the module cache and enforce a reload of the modules from source. This is needed, because otherwise modules will not reflect changes on the filesystem and inplace editing without Firefox restart is impossible.

Each object injected via MolyBase will have a property MolyBase. This way, you have to lookup MolyBase via Components only for the very first object in a dependency chain.

Caveats

Lifecycle

Normally, your javascript objects are initialized with the load of the window they are attached to. Scripts loaded with MolyBase are not attached to a window. Instaed of this, they are attached to the application lifecycle (in our case Firefox). This means, object are living as long as Firefox is stopped.
Even closing the Molybdenum window is not destroying the Javascript objects instanciated via MolyBase. This means: If you migrate your scripts to MolyBase, check object initialization mechanisms twice!

Singletons vs Prototypes

Dependency injection in javascript injects object into objects, no matter whether any of it is prototypical or not. However, MolyBase does not create a new instance of the loaded constructor function using "new". As such it provides very simple Singleton behaviour.

If you need to create a new instance of an injected prototypical object, you need to do this yourself. This is great for using MolyBase.inject(..) for importing other objects and then instantiating them manually (like MolyLogger instantiated and constructor-intialized with log category) each and every time.

However, for Singletons, you don't want MolyBase to construct new instances. Instead the injecting code will use the collaborator directly. Therefore, it does not make sense for a Singleton to use object prototyping.

Next steps

  • Move code from js directory to modules following the guidelines.
  • remove the corresponding <script> tags from molybdenum.xul.
  • Adapt unit tests by removing subscriptloader calls because transitive deps will be loaded automatically
  • use the singleton feature of modules, first thing could be the MolyLogger to avoid permanent instantiation
last edited on May 14, 2009 14:15
Adaptavist Theme Builder Powered by Atlassian Confluence