GUIDES Creating A New Uize Module
- Contents
- 1. Introduction
- 2. What Type of Module Are You Creating?
-
3. Name Your Module
- 3.1. Find the Appropriate Namespace
- 3.2. Choose a Name
- 4. Update Header Comments
- 5. Implement the Module
- 6. Document the Module
- 7. Create Examples for the Module
- 8. Write Unit Tests
- 9. Revisit This Guide, As Needed
1. Introduction
This document provides guidelines and advice for the development of new UIZE modules, to be followed by developers of the UIZE JavaScript Framework.
2. What Type of Module Are You Creating?
The first step in creating a new UIZE module is to decide what type of module you are creating.
Depending on your needs, you may wish to create a class module, a package module, an extension module, a namespace module, a library module, etc. If you need a refresher on modules, what's inside them, how they're loaded, the different types, etc., refer back to the JavaScript Modules guide.
2.1. Creating Widget Modules
If you're creating a new widget, then you'll be creating a class module, where the class is either a direct subclass of the Uize.Widget
base class, or is a subclass of some other existing widget class.
3. Name Your Module
Your new UIZE module needs a name, of course, and naming your module requires you to find the appropriate namespace and choose a name.
3.1. Find the Appropriate Namespace
All modules of the UIZE JavaScript Framework must exist somewhere under the Uize
namespace.
When choosing the namespace for your new UIZE module, keep in mind the following considerations and conventions...
3.1.1. Avoid Using the Uize Root Namespace
When adding a new module to the UIZE JavaScript Framework, avoid adding it directly under the Uize
root namespace if this can at all be avoided.
Before placing a module into the Uize
root namespace, exhaust all other options discussed in the sections below. There is usually a better place to put a module than directly under Uize
. Generally, modules directly under the Uize
root namespace should either be incredibly important and essential, or they should defined namespaces that are likely to grow into substantial subtrees in the module hierarchy (consider the examples of the Uize.Templates
, Uize.Test
, and Uize.Widget
namespaces).
3.1.2. Namespace for Extension Modules
Extension modules always exist in the namespace of the module they extend.
For example, the Uize.Fade.xFactory
extension module extends the Uize.Fade
class, therefore it is placed directly under the Uize.Fade
namespace. Similarly, the Uize.Color.xHsv
extension module extends the Uize.Color
object, so it is placed directly under that namespace.
3.1.3. Namespace for Class Modules
Class modules often, but do not have to, exist under the namespace of the classes that they subclass.
For example, the Uize.Widget.Bar
module defines a class that is a subclass of the Uize.Widget
class, so it is placed under that namespace. Similarly, the Uize.Widget.Bar.Slider
module defines a class that is a subclass of the Uize.Widget.Bar
class, so it is placed under that namespace, and so on.
There are times when this general rule is deviated from, such as when class modules are placed under namespaces for areas of functionality, rather than directly under the namespace of the class that they subclass. An example of this is the Uize.Util.Coupler
module. This module defines the Uize.Util.Coupler
class, which is a subclass of the Uize.Class
base class - not a subclass of Uize.Util
. The Uize.Util
module merely defines a namespace that serves as a place to group / organize various utility type modules. The modules under this namespace may be any type
of module.
3.1.4. Namespaces for Areas of Functionality
Certain dedicated namespaces exist for grouping / organization modules according to their area of functionality, such as, for example, the Uize.Color
, Uize.Data
, and Uize.Util
modules.
3.1.4.1. Namespace Modules Can Define Classes or Objects
In some cases, a module that defines a namespace for an area of functionality also defines a class or object.
For example, the Uize.Color
module defines the Uize.Color
object, but also provides a namespace for additional modules that provide color related functionality.
3.1.4.2. Namespace Modules Can Define Packages
In some cases, a module that defines a namespace for an area of functionality also defines a package that offers a base level of functionality in the form of static methods, and expects other modules under that namespace to offer additional related functionality.
An example of this is the Uize.Data
module, which contains a number of basic methods that are useful when dealing with data structures. Other modules under the Uize.Data
namespace, such as the Uize.Data.Csv
and Uize.Data.NameValueRecords
modules, provide more advanced or more specific functionality, but which is still centered around dealing with data structures.
3.1.4.3. Namespace Modules Can Be Purely Namespaces
In some cases, a module that defines a namespace for an area of functionality does nothing more than define a namespace, with the expectation that modules under that namespace will provide the functionality.
Examples of this are the Uize.Templates
and Uize.Util
modules, which only define namespaces and provide no actual functionality themselves.
3.1.5. Namespaces for Package Modules
When adding a new package module to the UIZE JavaScript Framework, first consider the general rule to avoid using the uize root namespace, and then try to use one of the already established namespaces for areas of functionality as a place to house your new module.
Package modules that provide additional, more advanced or more esoteric functionality can exist under the namespace of a module that offers related but more basic functionality. For example, the Uize.Data.Csv
package module was added under the Uize.Data
namespace, because it also offers data-related functionality.
Another example is the Uize.Data.PathsTree
package module. Extending this approach even further, the Uize.Data.PathsTree.CompactString
package module was added under the Uize.Data.PathsTree
namespace, because the Uize.Data.PathsTree.CompactString
module provides additional paths tree related functionality. And, this module actually directly requires the Uize.Data.PathsTree
module for features that it provides. So, in the absence of class inheritance, a namespace hierarchy can be established around the dependency relationships and a sensible layering of functionality.
3.1.6. Namespace for Test Modules
All test modules that test regular / non-test modules of the UIZE JavaScript Framework are placed within the Uize.Test
namespace.
The namespace hierarchy for test modules mirrors the namespace hierarchy of regular / non-test modules (see the section Unit Test Module for more info). The inheritance pattern is not the same, however. For example, the Uize.Widget.Bar
module would define a subclass of Uize.Widget
, but the Uize.Test.Uize.Widget.Bar
module would not define a subclass of Uize.Test.Uize.Widget
, since all test modules define subclasses of the Uize.Test
base class.
3.1.7. Namespace for JavaScript Template Modules
All JavaScript template modules added to the UIZE JavaScript Framework should be placed somewhere within the Uize.Templates
namespace.
While JavaScript template modules merely define packages and not classes, template modules can potentially extend on other template modules. For example, a certain flavor of dialog HTML template may build upon the HTML generated by a basic dialog HTML template. In such cases, the specific dialog template module could be placed under the namespace of the more general dialog template module, and it would then be coded to require the general dialog template module and would use that module in generating its output.
3.2. Choose a Name
Once you find the appropriate namespace for your new UIZE module, you will need to decide the name of the property of that namespace object that will reference your module.
For example, with the module Uize.Data.Csv
, the Uize.Data
module is the namespace, and the property Csv
of that namespace object is a reference to the module named Uize.Data.Csv
. If you're placing your new module under a namespace that already has a number of other modules under it, you'll need to come up with a name for a property that doesn't conflict with other properties already on the namespace object. By convention, property names for modules always begin with a capital letter, with the exception of extension modules, that begin with a lowercase "x".
4. Update Header Comments
When you use an existing module as the basis for creating a new module, make sure to update the header comments appropriately.
Don't just leave your new module with information that lingers from the module you used as your template, where this lingering information is incorrect for the new module you are creating. Pay attention to the considerations noted below...
4.1. Module Info Comment
Make sure that all information in the module info comment (the one that sits right at the very top of the file) is correct, paying attention to the module name comment line and the copyright notice comment line.
EXAMPLE
/*______________ | ______ | U I Z E J A V A S C R I P T F R A M E W O R K | / / | --------------------------------------------------- | / O / | MODULE : Uize.Widget.ListEditor Class | / / / | | / / / /| | ONLINE : http://uize.com | /____/ /__/_| | COPYRIGHT : (c)2006-2015 UIZE | /___ | LICENSE : Available under MIT License or GNU General Public License |_______________| http://uize.com/license.html */
4.1.1. Module Name Comment Line
The Module Name Comment Line is the line in the module info comment that lists the name of the module defined by the JavaScript file.
| / O / | MODULE : Uize.Widget.ListEditor Class
In the example module info comment shown above, the module name comment line is the one with the text "MODULE : Uize.Widget.ListEditor Class". If you use an existing module as a starting point for creating a new module, be sure not to leave this comment line unchanged (and, therefore, incorrect).
4.1.2. Copyright Notice Comment Line
The Copyright Notice Comment Line is the line in the module info comment that provides a notice of copyright for the code contained inside the module, stating the copyright date range and the copyright holder for the module's code.
| /____/ /__/_| | COPYRIGHT : (c)2006-2015 UIZE
In the example module info comment shown above, the copyright notice comment line is the one with the text "COPYRIGHT : (c)2006-2015 UIZE". If you use an existing module as a starting point for creating a new module, be sure to update the date range in the copyright notice the correctly reflect the year in which the new module is being created.
4.1.2.1. Copyright Date Range
The copyright date range must reflect when the code in the module was initially created - which is not necessarily the date when the actually JavaScript file that defines the module was created.
For example, a new module could be created by factoring out code that used to exist in some other module. In such cases, the copyright date range used in the copyright notice comment line for the new module should be the copyright date range for the existing module from which the new module's code was taken.
4.1.2.2. Copyright Holder
The copyright holder portion of the copyright notice comment line identifies the holder of the rights to copy the contents of the file.
The copyright holder should always be "UIZE" for modules of the UIZE JavaScript Framework.
4.2. Scruncher Settings Comment
The scruncher settings comment contains settings that are used by the scruncher when it builds a scrunched version of a JavaScript module's source code.
/*ScruncherSettings Mappings="=c" LineCompacting="TRUE"*/
Among the scruncher settings that can be specified in this comment, the most common and typical is the mappings setting, which specifies a comma separated list of one or more mappings (but usually just one), where each mapping maps an identifier prefix to a scrunch prefix. In the above example, there is only one mapping, between the empty prefix and the scrunch prefix "c". It should be noted that it is implicit that the prefix is delimited from the suffix by a "_" (underscore) character, so that's why the value on the left of the "=" (equals sign) is an empty string. This particular mapping maps all identifiers of the form _[unscrunchedIdentifierName]
to scrunched identifiers of the form c_[scrunchedIdentifierName]
. For a refresher on scrunching JavaScript files, consult the guide All About Scrunching.
4.2.1. Scruncher Mappings for Class Modules
For class modules, it is the convention to use a single letter for the scrunch namespace, where that letter is one letter higher than the letter used for the class' superclass.
So, for example, the Uize.Widget.ListEditor
module defines a class that is a subclass of the Uize.Widget
base class. Further, the Uize.Widget
class is a subclass of the Uize.Class
base class. The Uize.Class
module uses the letter "a" for its scrunch namespace, so the Uize.Widget
subclass uses the letter "b", and the Uize.Widget.ListEditor
class uses the letter "c". This ensures that the scrunched identifier names for private properties and methods that are defined at the various different levels of this class hierarchy don't accidentally collide (scrunching is applied discretely to each file, after all).
EXAMPLE
/*ScruncherSettings Mappings="=c" LineCompacting="TRUE"*/
4.2.2. Scruncher Mappings for Package Modules
For package modules, the convention is to use the blank scrunch namespace.
Because package modules only define utility static methods and properties, and because they do not place private static methods or properties into an object along with the private static methods or properties of other modules, there is no need for them to use a unique scrunch namespace. By using the blank-to-blank scrunch namespace mapping, all identifiers prefixed with just an underscore will be scrunched down to scrunched identifiers prefixed with just an underscore. This same convention applies to namespace modules, library modules, alias modules, and data modules.
EXAMPLE
/*ScruncherSettings Mappings="=" LineCompacting="TRUE"*/
4.3. Module Meta Data Comment
The module meta data comment is required for all UIZE modules and contains meta data information that is used on the SOTU (State of the UIZE) page.
The meta data contained in the module meta data comments of all UIZE JavaScript modules is scooped up and presented in a sortable table in the SOTU (State of the UIZE) page. This information can then be used by UIZE framework developers to get a big picture view of where things stand with the framework, and can help in determining where development effort should be directed.
The module meta data comment has the following syntax...
/* Module Meta Data type: [module type] importance: [0-10] codeCompleteness: [0-100] docCompleteness: [0-100] */
4.3.1. Module Meta Data Comment Entries
4.3.1.1. type
The type of the JavaScript module.
The value for this meta data entry should be one of the following, depending on the module...
Class - for modules that implemented classes | |
Package - for modules that provide utility static methods and/or properties | |
Extension - for modules that extend classes or packages | |
Data - for modules that define data objects | |
Object - for modules that define objects (as opposed to classes), such as the Uize.Color object module |
|
Template - for JavaScript template modules that are generated from .js.jst files |
|
Test - for unit test or functional test modules (subclasses of the Uize.Test class) |
|
Namespace - for modules that define namespaces | |
Alias - for modules that are merely aliases to other modules (provided for backwards compatibility) | |
Library - for modules that define libraries of modules that are to be bundled together by the build system |
4.3.1.2. importance
A number, in the range of 0
to 10
, indicating the overal importance of the module in the larger scheme of things.
To get a sense of how modules should be ranked in importance, take a look at the SOTU (State of the UIZE) page and note the importance that is assigned to modules with which you are familiar. A module like the Uize
module, for example, is assigned the importance of 10
. Without the Uize
module and the namespace it defines, pretty much nothing else in the framework can exist, so this is the most important module and has the highest importance possible.
On the opposite end of the spectrum, a module like the Uize.Str.Discombobulator
package is assigned an importance of 0
. This module provides some very esoteric and hardly used functionality, and so it has the lowest importance possible. It's important to emphasize that having an importance of 0
doesn't mean that a module is useless and has no value - it just means that it is among the least important modules in the framework, in the larger scheme of things.
In the middle of the importance spectrum are modules like Uize.Str.Lines
, Uize.Fx
, Uize.Date
, Uize.Tooltip
, and many others. Towards the high end of the importance spectrum, but not right at the top, are modules like Uize.Dom.Basics
, Uize.Widget
, Uize.Test
, Uize.Template
, and others. Assigning an importance ranking to a module is not an exact science, and the rankings are likely to shift over time as the framework evolves and expands. Just make the best assessment you can.
4.3.1.3. codeCompleteness
A number, in the range of 0
to 100
, indicating the code completeness of the module as a percentage.
The codeCompleteness
value is a best guess estimate of how complete a module's code is. One could argue that a module is never complete, because it can always be improved and features can always be added to it. The codeCompleteness
measure, however, is a measure of how complete the code is for an established feature set at any given point in time. Generally, a module should be 100% code complete before it is officially made public as part of the framework. This principle can be seen when viewing the SOTU page, where you will notice that the overwhelming majority of modules are marked as being 100% code complete.
4.3.1.3.1. Code Completeness for Test Modules
For test modules, the codeCompleteness
value is a best guess estimate of how complete the unit test module is with respect to the feature set of the module it is intended to test.
To determine a value, you can consider what percentage of the static methods, static properties, instance methods, instance properties, etc. of the module being tested are tested by the test module. Bear in mind that if, say, 50% of the features of a module are tested, you may still wish to assign a codeCompleteness
value for the test module that is less than or greater than 50%, based upon how much coding work you feel there is to writing the remaining tests. Also keep in mind that it is far more acceptable for test modules to not be 100% code complete than it is for other types of modules.
4.3.1.4. docCompleteness
A number, in the range of 0
to 100
, indicating the completeness of documentation for the module as a percentage.
The docCompleteness
value is a best guess estimate of how complete the documentation for the module is. To determine a value, you can consider what percentage of a module's static methods, static properties, instance methods, instance properties, etc. are documented. Bear in mind that if, say, 50% of the features of a module are documented, you may still wish to assign a docCompleteness
value for the module that is less than or greater than 50%, based upon how much work you feel there is to writing the remaining documentation.
4.3.2. So Long, One Hundred Percent
Attaining the sought after value of 100
for either of the codeCompleteness
, codeCompleteness
, or docCompleteness
meta data entries is unfortunately not the end of the story.
The value of any one of these entries may have to be bumped back down again as the framework evolves. A module that used to be code complete, for example, may cease to be code complete if its functionality is expanded but not completely implemented. Similarly, test completeness may be bumped back down from 100% if the module's functionality is expanded and the unit tests are not completely updated to keep up with the added features. The same effect can apply to documentation completeness, if new functionality is added without fully documenting it.
4.3.3. Module Meta Data in JavaScript Template Modules
Getting the module meta data comment into JavaScript template modules is a little bit trickier than regular JavaScript modules, but still quite doable.
The module meta data comment for a JavaScript template module can be placed in the JST source code as follows...
EXAMPLE
<%@ input ({idPrefix:'string'}) %><%/* Module Meta Data type: Template importance: 2 codeCompleteness: 100 docCompleteness: 100 */%><%@ /* guts of template follows... */ %>
It's admittedly a little bit ugly / awkward, but it works! In the example shown above, the module meta data comment is close to the top of the JavaScript template file, but after the input
directive. It doesn't have to be after the directive, but it feels right to have the directives first. Notice how there is no whitespace between the tokens that enclose the directive, the module meta data comment, and what follows the comment. This prevents unwanted extra whitespace from creeping into the generated JavaScript template module.
5. Implement the Module
When implementing your new UIZE module, there are a number of considerations to keep in mind...
Follow the Coding Style Guide - Be sure to be aware of the recommended coding conventions for JavaScript code. For a refresher, consult the JavaScript Code Conventions appendix. | |
Write Optimized Code - Make sure to optimize your code for both performance and code size. To learn some useful optimization tips and tricks, consult the JavaScript Optimization appendix. | |
Write Scruncher-ready Code - Make sure your UIZE module's code is Scruncher-ready. For a refresher on scrunching, consult the guide All About Scrunching. |
6. Document the Module
New modules added to the UIZE JavaScript Framework should, ideally, be comprehensively document.
When documenting your new UIZE module, keep the following considerations in mind...
6.1. At Least Have a Skeleton
If time does not allow every feature of a new module to be documented in a lot of detail, at least make sure that every feature is mentioned, along with some minimal information about the feature.
Build up the skeleton of your documentation in the following order...
For methods, at least have a basic summary line about the method, along with a SYNTAX sample code block. |
6.2. Add Flesh to the Skeleton
Once you have the basic skeleton of the documentation, convering in a very sparse way all the features of the module, you can then proceed to flesh out the documentation in the following order...
6.3. Update the docCompleteness Meta Data Entry
Once you've written as much documentation as you're going to write, make sure to update the value for the docCompleteness
meta data entry to reflect how complete the documentation is, using the following rough guide to completeness milestones...
docCompleteness: 2 - You only wrote the most basic "Introduction" paragraph, with no in-depth discussion of the module. |
|
docCompleteness: 20 - You wrote the basic "Introduction" paragraph, along with a skeleton that lists all the features of the module, such as static and instance methods, static and instance properties, state properties, static and instance events, DOM nodes (for widget classes), etc. |
|
docCompleteness: 80 - You wrote the basic "Introduction" paragraph, along with thorough reference documentation for all the features of the modules. There is not yet a thorough introduction and explanation of the module. |
|
docCompleteness: 100 - You wrote thorough reference documentation for all the features of the modules, and you wrote a thorough introduction and explanation of the module. Therefore, the documentation is complete! (yay) |
7. Create Examples for the Module
New modules added to the UIZE JavaScript Framework should have at least one example page to serve as a real life test and demonstration of the module's key capabilities.
An example page for a module should be only as complicated as is necessary to provide a compelling / convincing demonstration for the module, and as simple as possible so that a developer considering using the module doesn't have to wade through unnecessary complexity in order to get to the crux of how a module is used.
7.1. Modules That Don't Warrant Examples
Example pages are neither necessary nor particularly useful for certain kinds of modules, including data modules, template modules, test modules, namespace modules, alias modules, and library modules.
For template modules that generate HTML for specific widget classes, these template modules can be demonstrated along with the widget class modules for which they are intended. For template modules that are not widget HTML generators, example pages can be created.
8. Write Unit Tests
New modules added to the UIZE JavaScript Framework should be thoroughly unit tested.
This involves the creation of a companion unit test module for your new module, and the incorporation of that unit test module into the UIZE unit tests.
8.1. Unit Test Module
Each UIZE module should have a companion test module (except test modules themselves, of course).
A unit test module for a UIZE module should be placed under the Uize.Test
namespace, and should be named by appending the module name to "Uize.Test.". So, for example, the unit test module for the module Uize.Data
would be named Uize.Test.Uize.Data
. To get a sense for how unit test modules should be implemented, take a look through the source code of existing test modules. When implementing your unit test module, be sure to follow these same guidelines for creating regular UIZE modules, since many of the same principles apply, but keep the following points in mind...
Test modules only need to have the most basic "Introduction" paragraph in order to be considered documentation complete (see the Uize.Test.Uize.Data module for an example of documentation for a test module). |
|
The value for the importance meta data entry in the module meta data comment for a test module should never be greater than the importance value for the module it tests, and should generally be somewhat less. |
|
Test modules do not need example pages. |
8.2. UIZE Unit Tests
The unit test suite for the UIZE JavaScript Framework is implemented in the Uize.Test.UnitTests
module.
The Uize.Test.UnitTests
module is a test module that defines an uber test comprised of all the test modules that test individual modules of the framework, and is used by build scripts that automate testing of the framework. When creating a new UIZE module and, therefore, creating its companion unit test module, the unit test module needs to be entered into the Uize.Test.UnitTests
module. This involves adding a line to the file in order to add the test to the sequence of tests performed by the Uize.Test.UnitTests
module. When you open up the source code for the Uize.Test.UnitTests
module, it should be pretty clear what to do.
9. Revisit This Guide, As Needed
Creating a new module for the UIZE JavaScript Framework is no trivial matter - you're growing a framework that many developers will be building applications and features on.
Don't be afraid to revisit this document as a refresher when adding a new module, and as a last step / checklist before finally committing your new module to the framework. If it takes a few minutes to skim through this document and see if you've forgotten or missed anything, it might be a few minutes well spent.