Dojo's broken module naming convention
Level: Intermediate
Date: Originally
posted 3/12/08, updated 4/08.
Author: Richard Bondi,
There is a catch-22 in Dojo for what convention to use when naming a
module and its classes. Before I can explain it, I need to describe
what dojo.require(), dojo.provide(), and dojo.declare() actually do.
Then I'll return to the convention problems surrounding what arguments
-- namespaces, actually -- to pass them. That way, you'll be able to
make an informed decision on how to handle the convention problem
yourself. Note that this post assumes you know what a
Dojo class is, and how to make one with dojo.declare().
An overview of Dojo modules
A module is a .js file that contains many functions that are part of a Javascript object. Typically the object is declared using
dojo.declare(), in which case it is called a
Dojo class (more on this below). The first line of a module file is a
dojo.provide() call, whose argument corresponds to the module file's name. So dojo.provide("a.b.c.d") would typically mean there was a file d.js located in the directories [dojo root]/a/b/c.
For a completely different gumby.js file to have access to all the dojo classes in the module a.b.c.d, just put the dojo function
dojo.require("a.b.c.d") into gumby.js. (If you're a Java programmer, this is just like adding an import statement.)
Now let's take a closer look at these three functions.
dojo.require()
When you put dojo.require("foo.bar.mymod") in a .js file -- let's call it mymod.js -- , it first checks a global hash (named d._loadedModule, if you're interested) to see if an object foo.bar.mymod is inside.
If foo.bar.mymod is already in the hash, dojo.require() simply returns it. It needn't have bothered; it might as well do nothing, as we'll see.
If foo.bar.mymod isn't in the hash, dojo.require() figures out from namespaces what file "foo.bar.mymod" refers to. Namespaces are too large a topic to cover here, so let's just assume the file is "/foo/bar/mymod.js". Then dojo.require loads this file. When I say "load", I mean dojo.require acts just like this code would:
<html>
<script type="text/javascript" src="/foo/bar/mymod.js"></script>
</html>
Finally, to prevent itself from loading this file a second time, dojo.require sticks an object foo.bar.mymod into the hash d._loadedModule.
Actually, that's not
quite true, and that brings us to dojo.provide.
dojo.provide()
In fact, it isn't really dojo.require() that sticks the object foo.bar.mymod into the hash. It is dojo.provide("foo.bar.mymod"), the first line of mymod.js, that does it when dojo.require loads mymod.js.
The important thing to realize is that dojo.provide does practically nothing. All it does is add "foo.bar.mymod" to the hash d._loadedModule. Why? So that future dojo.require("foo.bar.mymod") statements won't load mymod.js twice. That's all dojo.provide() is good for! Really!
dojo.declare()
Which brings us to dojo.declare(). The file mymod.js is a so-called dojo module. It has only one dojo.provide() statement (for reasons that should be obvious now), but it can have many dojo.declare() statements. Each dojo.declare() creates a so-called dojo class, a function object that you can create instances of with the Javascript "new" keyword. In other words, you do: dojo.declare("X", ..., ... ) so that you can later do: var myX = new X(...).
The first parameter of dojo.declare is typically not "X" though, it is a long namespace. Thus dojo.declare("a.b.c.Mulch", ...) will create a Javascript global context function a.b.c.Mulch(). (If you don't know what the Javascript global context is, you should look it up; it is a fundamental Javascript concept.)
And here's the key point: The first parameter of dojo.declare() does not have to look anything like the parameter of dojo.provide(). dojo.provide makes a Javascript global context object that is used for almost nothing, as we just saw; so nothing, including the first parameter of dojo.declare, has to be identical to it. The object functions made by dojo.declare have nothing to do with the global context function made by dojo.provide. Nothing!
So a perfectly legal module in <strong>/a/b/c/d.js</strong> could look like:
[File: d.js]
// This is file /a/b/c/d.js
dojo.provide("a.b.c.d");
dojo.declare("gumby.Foo", ..., ... );
dojo.declare("z.y.x.Blah", ..., ... );
This will create three global context objects: a.b.c.d, gumby.Foo, and z.y.x.Blah, none of which refer to each other. They belong to the same module only in the sense that their source code is in the same module file, but that's it. Some of the module files in /dojox/grid look a bit like this.
Belaboring the point
Just to emphasize how unrelated dojo.provide and dojo.declare are to each other, consider what happens when dojo.require("a.b.c.d") runs for the first and second times in, say, files First.js and Second.js.
The first time, when First.js is loaded, all three global objects a.b.c.d, gumby.Foo, and z.y.x.Blah wil come into existence simply because dojo.require("a.b.c.d") loads the file d.js from directory /a/b/c. If you had a Javascript reference to a.b.c.d, though, you couldn't get from it to gumby.Foo or z.y.x.Blah. Dojo's code simply does not assume or require a connection between them.
When Second.js is loaded, dojo.require("a.b.c.d") runs again, but because the object a.b.c.d already exists, dojo.require just returns it. Can Second.js still find the other two objects/classes gumby.Foo and z.y.x.Blah? Of course it can, because they are global context objects. They need have nothing to do with a.b.c.d to be found and used.
Contradictory conventions
That said, there is a dojo convention to "provide what you declare." In other words, the argument of dojo.provide() should be the argument of the first dojo.declare().
So this convention says we should do:
[File: foo.js]
dojo.provide("a.b.c.foo");
dojo.declare("a.b.c.foo", .... );
That brings us to the catch 22.
Unfortunately, the dojo style guide (http://www.dojotoolkit.org/developer/StyleGuide#Quick_Reference) tells us that modules should all be lowercase, and classes should all be capitalized. That means we should perhaps instead be doing:
[File: foo.js]
dojo.provide("a.b.c.foo");
dojo.declare("a.b.c.foo.Foo", .... );
// or a.b.c.Foo?
// or like Java, a.b.c.foo.WhateverInCaps?
So we have two dojo conventions that contradict each other. What should you do? My answer: understand these three functions dojo.require, provide, and declare, and then adopt a consistent convention. Who am I to tell you what to do? There's a fair bit of inconsistency in Dojo itself here, as you will see once you start browsing around the code base.
A note on objects
Note that if both dojo.provide and dojo.declare use "a.b.c.foo", then they both end up producing the same object a.b.c.foo. Does this matter? No. Remember, dojo.provide just sticks its object in some hash as a flag, for dojo.require to check. The object is never used for anything else. Whether that object is a full-blooded, dojo.declare'd class, or just an empty shell, makes no difference to dojo.require -- or to dojo.
Note also that if you do dojo.provide("a.b.c.foo"), and all the dojo.declares in its module begin with "a.b.c.foo.", then all the dojo classes will be properties of the object that dojo.provide created, namely of a.b.c.foo. Is this good for anything? Not for anything in the Dojo codebase; a.b.c.foo is used by it as a flag, and nothing else. Plenty of modules in the dojo code base do not do this.
My personal suggestion, which is not a formal Dojo one: do this anyway. Among other things, this makes it easy to see at a glance what dojo.require() goes with what dojoType attribute. (With the dojo grid, these seem barely related, for example.) And it looks a bit like how Java does imports. But that's just IMHO.

This work is licensed under a
Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 United States License.