canmove, Confirmed users
2,056
edits
m (moved Labs/Jetpack/DesignGoals to Labs/Jetpack/Design Guidelines: contains more than just goals; consistency with naming conventation) |
(first version of specific recommendations) |
||
Line 25: | Line 25: | ||
* completely specified, in Jetpack Enhancement Proposals (JEPs) | * completely specified, in Jetpack Enhancement Proposals (JEPs) | ||
* completely documented, in Jetpack documentation | * completely documented, in Jetpack documentation | ||
= Recommendations = | |||
Note: these are recommendations, not rules, so you can ignore them, but you should do so only with good reason (and in consultation with Jetpack leads). | |||
Jetpack APIs are implemented by Cuddlefish modules conforming to the CommonJS specification. Modules that provide functionality via a singleton should provide it via the simplest distinct nonabbreviated name. | |||
let storage = require("simple-storage").storage; | |||
''[Myk: for modules that only export a single symbol, it would be even simpler for the <code>require</code> call to return the singleton itself. We could enable this with some Cuddlefish changes, but it violates CommonJS convention and would complicate migration to a native Javascript module implementation in the future, were one to be implemented, as such an implementation would be unlikely to support this behavior.]'' | |||
Modules that enable extensions to create multiple instances of an object should export a constructor function for it. Constructors should take a single parameter, <code>options</code>, that is a property collection (i.e. object) of name/value pairs with which to configure the properties of the instance. | |||
let Panel = require("panel").Panel; | |||
let panel = new Panel({ content: "...", width: 200, height: 300 }); | |||
// panel.content, panel.width, and panel.height are defined. | |||
Properties that don't have to remain constant over the life of an instance should be settable and deletable after construction by directly setting and deleting the properties. | |||
let Panel = require("panel").Panel; | |||
let panel = new Panel(); | |||
// Set some properties. | |||
panel.content = "..."; | |||
panel.width = 200; | |||
panel.height = 300; | |||
// Change the value of a property. | |||
panel.height = 400; | |||
// Delete a property. | |||
delete panel.height; | |||
It should be possible to construct instances of an object by calling its constructor without the <code>new</code> operator. | |||
let Panel = require("panel").Panel; | |||
let panel = Panel(); | |||
''[Myk: this is subject to investigation for implementation feasibility; jQuery does it, but I'm not sure we can reuse its implementation.]'' | |||
Method names should be verbs, e.g. <code>show</code>, <code>post</code>. Property names should be the simplest accurate conventional or idiomatic label for their value (e.g. <code>persistent</code> for the boolean property of a visual feature that specifies whether or not the feature remains visible all the time). | |||
Instances should have a <code>destroy</code> method that unloads any resources and breaks any cyclical references associated with the object. The method should be called automatically when the extension that instantiated the object is unloaded. Extensions should also be able to call it to unload those resources and break those references once the object is no longer required. | |||
let Panel = require("panel").Panel; | |||
let panel = new Panel(); | |||
panel.destroy(); | |||
Callback interfaces should be implemented as properties to which callback functions can be set. Callback properties should be settable and deletable like other properties. Callback properties should be named "on" followed by the CamelCase present-tense (if verb) name of the event/notification triggering the callback, e.g. onClick, onLoad, onSelect. | |||
let Panel = require("panel").Panel; | |||
// Set a callback on construction. | |||
let panel = new Panel({ onShow: function() { ... } }); | |||
// Set a callback after construction. | |||
panel.onShow = function() { ... }; | |||
// Remove a callback. | |||
delete panel.onShow; | |||
When callbacks are invoked, the <code>this</code> object of the function call should be the object on which the callback is registered. | |||
''[Myk: recommendations for positioning of callback arguments is TBD.]'' | |||
When capitalizing verbs in method and callback property names, only the first letter of verbs comprising a stem and affix (e.g. "upload") should be capitalized, while the first letters of all words of phrasal verbs (verbs comprising multiple words, like "sign in") should be capitalized, e.g. "onUpload" but "onSignIn". | |||
A quick technique for differentiating these is to consider the simple imperative sentence "[verb] it": "upload it" sounds right, while "sign in it" doesn't (it should be "sign it in"), suggesting that the former is a verb comprising a stem and affix while the latter is a phrasal verb. | |||
It should be possible to specify multiple callbacks for an event by assigning an array to the event's callback property or by calling its <code>add</code> and <code>remove</code> methods: | |||
let foo = function() { ... }; | |||
let bar = function() { ... }; | |||
const Panel = require("panel").Panel; | |||
let panel = new Panel({ onShow: [ foo, bar ] }); | |||
panel.show(); // foo and bar are both called. | |||
let baz = function() { ... }; | |||
panel.onShow.add(baz); | |||
panel.show(); // foo, bar, and baz are all called. | |||
panel.onShow.remove(bar); | |||
panel.show(); // Only foo and baz are called. | |||
''[Myk: the reasoning here is that the common case is for consumers to register a single callback, so that should be the simplest option, but there are use cases for consumers to register multiple callbacks, so that should also be possible. jQuery uses bind/unbind as callback addition/removal methods for certain kinds of objects, but "bind" is a built-in in the new ECMAScript 5, so we shouldn't use it here to avoid conflation with that meaning. jQuery's API also supports generic and namespaced event handlers, but we don't currently have well-defined common use cases for those features, so we shouldn't support them at this time.]'' | |||
Interfaces that accept rich content for display (e.g. panel, visual-feature) should do so via a property named <code>content</code>. The property should accept a string of HTML, an E4X XML object, or a URL from which the content should be loaded. | |||
Interfaces should take advantage of composition to reduce repetition and complexity. For example, interfaces that display a panel should solicit it from their consumers in the form of a property that accepts a Panel object. | |||
= References = | = References = |