[ADD] document documentation (esp. JS APIDoc via JsDoc, which is actually a fairly complex endeavour)
bzr revid: xmo@openerp.com-20110327120515-wdcxc19j4exuu2zu
This commit is contained in:
parent
ab43c4ee48
commit
bdddf2269d
|
@ -65,6 +65,10 @@
|
||||||
// OpenERP initialisation and black magic about the pool
|
// OpenERP initialisation and black magic about the pool
|
||||||
//---------------------------------------------------------
|
//---------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name openerp
|
||||||
|
* @namespace
|
||||||
|
*/
|
||||||
(function() {
|
(function() {
|
||||||
if (this.openerp)
|
if (this.openerp)
|
||||||
return;
|
return;
|
||||||
|
@ -106,6 +110,7 @@
|
||||||
// OpenERP initialisation and black magic about the pool
|
// OpenERP initialisation and black magic about the pool
|
||||||
//---------------------------------------------------------
|
//---------------------------------------------------------
|
||||||
|
|
||||||
|
/** @namespace */
|
||||||
openerp.base = function(instance) {
|
openerp.base = function(instance) {
|
||||||
openerp.base$chrome(instance);
|
openerp.base$chrome(instance);
|
||||||
openerp.base$views(instance);
|
openerp.base$views(instance);
|
||||||
|
|
|
@ -5,17 +5,6 @@
|
||||||
openerp.base$chrome = function(openerp) {
|
openerp.base$chrome = function(openerp) {
|
||||||
|
|
||||||
openerp.base.callback = function(obj, method) {
|
openerp.base.callback = function(obj, method) {
|
||||||
// openerp.base.callback( obj, methods, [arg1, arg2, ... ] )
|
|
||||||
//
|
|
||||||
// The callback object holds a chain that can be altered:
|
|
||||||
// callback.add( handler , [arg1, arg2, ... ] )
|
|
||||||
// callback.add( {
|
|
||||||
// callback: function
|
|
||||||
// self: object or null
|
|
||||||
// args: array
|
|
||||||
// position: "first" or "last"
|
|
||||||
// unique: boolean
|
|
||||||
// })
|
|
||||||
var callback = function() {
|
var callback = function() {
|
||||||
var args = Array.prototype.slice.call(arguments);
|
var args = Array.prototype.slice.call(arguments);
|
||||||
var r;
|
var r;
|
||||||
|
@ -64,11 +53,13 @@ openerp.base.callback = function(obj, method) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
openerp.base.BasicController = Class.extend({
|
openerp.base.BasicController = Class.extend(
|
||||||
|
/** @lends openerp.base.BasicController# */{
|
||||||
/**
|
/**
|
||||||
* Controller contructor
|
|
||||||
* rpc operations, event binding and callback calling should be done in
|
* rpc operations, event binding and callback calling should be done in
|
||||||
* start() instead of init so that event can be hooked in between.
|
* start() instead of init so that event can be hooked in between.
|
||||||
|
*
|
||||||
|
* @constructs
|
||||||
*/
|
*/
|
||||||
init: function(element_id) {
|
init: function(element_id) {
|
||||||
this.element_id = element_id;
|
this.element_id = element_id;
|
||||||
|
@ -117,7 +108,15 @@ openerp.base.Console = openerp.base.BasicController.extend({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
openerp.base.Session = openerp.base.BasicController.extend({
|
openerp.base.Session = openerp.base.BasicController.extend(
|
||||||
|
/** @lends openerp.base.Session# */{
|
||||||
|
/**
|
||||||
|
* @constructs
|
||||||
|
* @extends openerp.base.BasicController
|
||||||
|
* @param element_id
|
||||||
|
* @param server
|
||||||
|
* @param port
|
||||||
|
*/
|
||||||
init: function(element_id, server, port) {
|
init: function(element_id, server, port) {
|
||||||
this._super(element_id);
|
this._super(element_id);
|
||||||
this.server = (server == undefined) ? location.hostname : server;
|
this.server = (server == undefined) ? location.hostname : server;
|
||||||
|
@ -259,6 +258,7 @@ openerp.base.Session = openerp.base.BasicController.extend({
|
||||||
/**
|
/**
|
||||||
* Fetches a cookie stored by an openerp session
|
* Fetches a cookie stored by an openerp session
|
||||||
*
|
*
|
||||||
|
* @private
|
||||||
* @param name the cookie's name
|
* @param name the cookie's name
|
||||||
*/
|
*/
|
||||||
get_cookie: function (name) {
|
get_cookie: function (name) {
|
||||||
|
@ -273,8 +273,9 @@ openerp.base.Session = openerp.base.BasicController.extend({
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Create a new secure cookie with the provided name and value
|
* Create a new cookie with the provided name and value
|
||||||
*
|
*
|
||||||
|
* @private
|
||||||
* @param name the cookie's name
|
* @param name the cookie's name
|
||||||
* @param value the cookie's value
|
* @param value the cookie's value
|
||||||
* @param ttl the cookie's time to live, 1 year by default, set to -1 to delete
|
* @param ttl the cookie's time to live, 1 year by default, set to -1 to delete
|
||||||
|
@ -328,7 +329,12 @@ openerp.base.Database = openerp.base.BasicController.extend({
|
||||||
// Non Session Controller to manage databases
|
// Non Session Controller to manage databases
|
||||||
});
|
});
|
||||||
|
|
||||||
openerp.base.Controller = openerp.base.BasicController.extend({
|
openerp.base.Controller = openerp.base.BasicController.extend(
|
||||||
|
/** @lends openerp.base.Controller# */{
|
||||||
|
/**
|
||||||
|
* @constructs
|
||||||
|
* @extends openerp.base.BasicController
|
||||||
|
*/
|
||||||
init: function(session, element_id) {
|
init: function(session, element_id) {
|
||||||
this._super(element_id);
|
this._super(element_id);
|
||||||
this.session = session;
|
this.session = session;
|
||||||
|
|
|
@ -115,12 +115,14 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
|
||||||
},
|
},
|
||||||
on_edit: function() {
|
on_edit: function() {
|
||||||
},
|
},
|
||||||
do_search: function (domains, contexts) {
|
do_search: function (domains, contexts, group_contexts) {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.rpc('/base/session/eval_domain_and_context', {
|
this.rpc('/base/session/eval_domain_and_context', {
|
||||||
domains: domains,
|
domains: domains,
|
||||||
contexts: contexts
|
contexts: contexts,
|
||||||
|
groupby: group_contexts
|
||||||
}, function (results) {
|
}, function (results) {
|
||||||
|
// TODO: group by
|
||||||
self.dataset.set({
|
self.dataset.set({
|
||||||
context: results.context,
|
context: results.context,
|
||||||
domain: results.domain
|
domain: results.domain
|
||||||
|
@ -137,11 +139,19 @@ openerp.base.ViewManagerRoot = openerp.base.Controller.extend({
|
||||||
openerp.base.ViewManagerUsedAsAMany2One = openerp.base.Controller.extend({
|
openerp.base.ViewManagerUsedAsAMany2One = openerp.base.Controller.extend({
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
openerp.base.DataSet = openerp.base.Controller.extend(
|
||||||
* Management interface between views and the collection of selected OpenERP
|
/** @lends openerp.base.DataSet# */{
|
||||||
* records (represents the view's state?)
|
|
||||||
*/
|
/**
|
||||||
openerp.base.DataSet = openerp.base.Controller.extend({
|
* Management interface between views and the collection of selected OpenERP
|
||||||
|
* records (represents the view's state?)
|
||||||
|
*
|
||||||
|
* @constructs
|
||||||
|
* @extends openerp.base.Controller
|
||||||
|
*
|
||||||
|
* @param {openerp.base.Session} session current OpenERP session
|
||||||
|
* @param {String} model the OpenERP model this dataset will manage
|
||||||
|
*/
|
||||||
init: function(session, model) {
|
init: function(session, model) {
|
||||||
this._super(session);
|
this._super(session);
|
||||||
this.model = model;
|
this.model = model;
|
||||||
|
@ -205,10 +215,9 @@ openerp.base.DataSet = openerp.base.Controller.extend({
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @event
|
|
||||||
*
|
|
||||||
* Fires after the DataSet fetched the records matching its internal ids selection
|
* Fires after the DataSet fetched the records matching its internal ids selection
|
||||||
*
|
*
|
||||||
|
* @event
|
||||||
* @param {Array} records An array of the DataRecord fetched
|
* @param {Array} records An array of the DataRecord fetched
|
||||||
* @param event The on_fetch event object
|
* @param event The on_fetch event object
|
||||||
* @param {Number} event.offset the offset with which the original DataSet#fetch call was performed
|
* @param {Number} event.offset the offset with which the original DataSet#fetch call was performed
|
||||||
|
@ -239,10 +248,9 @@ openerp.base.DataSet = openerp.base.Controller.extend({
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @event
|
|
||||||
*
|
|
||||||
* Fires after the DataSet fetched the records matching its internal active ids selection
|
* Fires after the DataSet fetched the records matching its internal active ids selection
|
||||||
*
|
*
|
||||||
|
* @event
|
||||||
* @param {Array} records An array of the DataRecord fetched
|
* @param {Array} records An array of the DataRecord fetched
|
||||||
*/
|
*/
|
||||||
on_active_ids: function (records) { },
|
on_active_ids: function (records) { },
|
||||||
|
@ -268,6 +276,7 @@ openerp.base.DataSet = openerp.base.Controller.extend({
|
||||||
/**
|
/**
|
||||||
* Fires after the DataSet fetched the record matching the current active record
|
* Fires after the DataSet fetched the record matching the current active record
|
||||||
*
|
*
|
||||||
|
* @event
|
||||||
* @param record the record matching the provided id, or null if there is no record for this id
|
* @param record the record matching the provided id, or null if there is no record for this id
|
||||||
*/
|
*/
|
||||||
on_active_id: function (record) {
|
on_active_id: function (record) {
|
||||||
|
@ -391,7 +400,25 @@ openerp.base.DataRecord = openerp.base.Controller.extend({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
openerp.base.SearchView = openerp.base.Controller.extend({
|
openerp.base.SearchView = openerp.base.Controller.extend(
|
||||||
|
/** @lends openerp.base.SearchView# */{
|
||||||
|
/**
|
||||||
|
* Manager for the Search view type.
|
||||||
|
*
|
||||||
|
* Handles laying out and rendering the various search widgets, as well
|
||||||
|
* as collecting contexts and domain and broadcasting them to whoever
|
||||||
|
* registered itself on the :js:func:`~openep.base.SearchView.on_search`
|
||||||
|
* event.
|
||||||
|
*
|
||||||
|
* @constructs
|
||||||
|
* @extends openerp.base.Controller
|
||||||
|
*
|
||||||
|
* @param {openerp.base.Session} session the current OpenERP session
|
||||||
|
* @param {String} element_id the root element where the search view should render itself
|
||||||
|
* @param {openerp.base.DataSet} dataset the dataset with which the search view will work
|
||||||
|
* @param {Number} view_id the id of this view object
|
||||||
|
* @param {Object} defaults default values to set on the various fields of the view
|
||||||
|
*/
|
||||||
init: function(session, element_id, dataset, view_id, defaults) {
|
init: function(session, element_id, dataset, view_id, defaults) {
|
||||||
this._super(session, element_id);
|
this._super(session, element_id);
|
||||||
this.dataset = dataset;
|
this.dataset = dataset;
|
||||||
|
@ -539,7 +566,13 @@ openerp.base.SearchView = openerp.base.Controller.extend({
|
||||||
map(function (input) { return input.get_context(); }).
|
map(function (input) { return input.get_context(); }).
|
||||||
compact().
|
compact().
|
||||||
value();
|
value();
|
||||||
this.on_search(domains, contexts);
|
// TODO: group_by on field contexts?
|
||||||
|
var group_contexts = _(this.enabled_filters).
|
||||||
|
chain().
|
||||||
|
map(function (filter) { return filter.get_context(); }).
|
||||||
|
compact().
|
||||||
|
value();
|
||||||
|
this.on_search(domains, contexts, group_contexts);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Event hook for searches: triggers after the SearchView has collected
|
* Event hook for searches: triggers after the SearchView has collected
|
||||||
|
@ -551,8 +584,9 @@ openerp.base.SearchView = openerp.base.Controller.extend({
|
||||||
*
|
*
|
||||||
* @param {Array} domains an array of string or literal domains
|
* @param {Array} domains an array of string or literal domains
|
||||||
* @param {Array} contexts an array of string or literal contexts
|
* @param {Array} contexts an array of string or literal contexts
|
||||||
|
* @param {Array} group_contexts an ordered array of contexts which may need to be used to resolve grouping
|
||||||
*/
|
*/
|
||||||
on_search: function (domains, contexts) { },
|
on_search: function (domains, contexts, group_contexts) { },
|
||||||
do_clear: function (e) {
|
do_clear: function (e) {
|
||||||
if (e && e.preventDefault) { e.preventDefault(); }
|
if (e && e.preventDefault) { e.preventDefault(); }
|
||||||
this.on_clear();
|
this.on_clear();
|
||||||
|
@ -583,8 +617,19 @@ openerp.base.SearchView = openerp.base.Controller.extend({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** @namespace */
|
||||||
openerp.base.search = {};
|
openerp.base.search = {};
|
||||||
openerp.base.search.Invalid = Class.extend({
|
openerp.base.search.Invalid = Class.extend(
|
||||||
|
/** @lends openerp.base.search.Invalid# */{
|
||||||
|
/**
|
||||||
|
* Exception thrown by search widgets when they hold invalid values,
|
||||||
|
* which they can not return when asked.
|
||||||
|
*
|
||||||
|
* @constructs
|
||||||
|
* @param field the name of the field holding an invalid value
|
||||||
|
* @param value the invalid value
|
||||||
|
* @param message validation failure message
|
||||||
|
*/
|
||||||
init: function (field, value, message) {
|
init: function (field, value, message) {
|
||||||
this.field = field;
|
this.field = field;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
|
@ -595,8 +640,17 @@ openerp.base.search.Invalid = Class.extend({
|
||||||
': [' + this.value + '] is ' + this.message);
|
': [' + this.value + '] is ' + this.message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
openerp.base.search.Widget = openerp.base.Controller.extend({
|
openerp.base.search.Widget = openerp.base.Controller.extend(
|
||||||
|
/** @lends openerp.base.search.Widget# */{
|
||||||
template: null,
|
template: null,
|
||||||
|
/**
|
||||||
|
* Root class of all search widgets
|
||||||
|
*
|
||||||
|
* @constructs
|
||||||
|
* @extends openerp.base.Controller
|
||||||
|
*
|
||||||
|
* @param view the ancestor view of this widget
|
||||||
|
*/
|
||||||
init: function (view) {
|
init: function (view) {
|
||||||
this.view = view;
|
this.view = view;
|
||||||
},
|
},
|
||||||
|
@ -682,7 +736,14 @@ openerp.base.search.Group = openerp.base.search.Widget.extend({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
openerp.base.search.Input = openerp.base.search.Widget.extend({
|
openerp.base.search.Input = openerp.base.search.Widget.extend(
|
||||||
|
/** @lends openerp.base.search.Input# */{
|
||||||
|
/**
|
||||||
|
* @constructs
|
||||||
|
* @extends openerp.base.search.Widget
|
||||||
|
*
|
||||||
|
* @param view
|
||||||
|
*/
|
||||||
init: function (view) {
|
init: function (view) {
|
||||||
this._super(view);
|
this._super(view);
|
||||||
this.view.inputs.push(this);
|
this.view.inputs.push(this);
|
||||||
|
@ -747,12 +808,21 @@ openerp.base.search.Filter = openerp.base.search.Input.extend({
|
||||||
return this.attrs.domain;
|
return this.attrs.domain;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
openerp.base.search.Field = openerp.base.search.Input.extend({
|
openerp.base.search.Field = openerp.base.search.Input.extend(
|
||||||
|
/** @lends openerp.base.search.Field# */ {
|
||||||
template: 'SearchView.field',
|
template: 'SearchView.field',
|
||||||
default_operator: '=',
|
default_operator: '=',
|
||||||
// TODO: set default values
|
// TODO: set default values
|
||||||
// TODO: get context, domain
|
// TODO: get context, domain
|
||||||
// TODO: holds Filters
|
// TODO: holds Filters
|
||||||
|
/**
|
||||||
|
* @constructs
|
||||||
|
* @extends openerp.base.search.Input
|
||||||
|
*
|
||||||
|
* @param view_section
|
||||||
|
* @param field
|
||||||
|
* @param view
|
||||||
|
*/
|
||||||
init: function (view_section, field, view) {
|
init: function (view_section, field, view) {
|
||||||
this._super(view);
|
this._super(view);
|
||||||
this.attrs = _.extend({}, field, view_section.attrs);
|
this.attrs = _.extend({}, field, view_section.attrs);
|
||||||
|
@ -795,7 +865,18 @@ openerp.base.search.Field = openerp.base.search.Input.extend({
|
||||||
return this.attrs['filter_domain'];
|
return this.attrs['filter_domain'];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
openerp.base.search.CharField = openerp.base.search.Field.extend({
|
/**
|
||||||
|
* Implementation of the ``char`` OpenERP field type:
|
||||||
|
*
|
||||||
|
* * Default operator is ``ilike`` rather than ``=``
|
||||||
|
* * The Javascript and the HTML values are identical (strings)
|
||||||
|
*
|
||||||
|
* @class
|
||||||
|
* @extends openerp.base.search.Field
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
openerp.base.search.CharField = openerp.base.search.Field.extend(
|
||||||
|
/** @lends openerp.base.search.CharField# */ {
|
||||||
default_operator: 'ilike',
|
default_operator: 'ilike',
|
||||||
get_value: function () {
|
get_value: function () {
|
||||||
return this.$element.val();
|
return this.$element.val();
|
||||||
|
|
|
@ -19,6 +19,193 @@ Merge proposals
|
||||||
Writing documentation
|
Writing documentation
|
||||||
+++++++++++++++++++++
|
+++++++++++++++++++++
|
||||||
|
|
||||||
|
The OpenERP Web project documentation uses Sphinx_ for the literate
|
||||||
|
documentation (this document for instance), the development guides
|
||||||
|
(for Python and Javascript alike) and the Python API documentation
|
||||||
|
(via autodoc_).
|
||||||
|
|
||||||
|
For the Javascript API, documentation should be written using the
|
||||||
|
`JsDoc Toolkit`_.
|
||||||
|
|
||||||
|
Guides and main documentation
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The meat and most important part of all documentation. Should be
|
||||||
|
written in plain english, using reStructuredText_ and taking advantage
|
||||||
|
of `Sphinx's extensions`_, especially `cross-references`_.
|
||||||
|
|
||||||
|
Python API Documentation
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
All public objects in Python code should have a docstring written in
|
||||||
|
RST, using Sphinx's `Python domain`_ [#]_:
|
||||||
|
|
||||||
|
* Functions and methods documentation should be in their own
|
||||||
|
docstring, using Sphinx's `info fields`_
|
||||||
|
|
||||||
|
For parameters types, built-in and stdlib types should be using the
|
||||||
|
combined syntax::
|
||||||
|
|
||||||
|
:param dict foo: what the purpose of foo is
|
||||||
|
|
||||||
|
unless a more extensive explanation needs to be given (e.g. the
|
||||||
|
specification that the input should be a list of 3-tuple needs to
|
||||||
|
use ``:type:`` even though all types involved are built-ins). Any
|
||||||
|
other type should be specified in full with a cross-reference using
|
||||||
|
the ``:type:`` field::
|
||||||
|
|
||||||
|
:param foo: what the purpose of foo is
|
||||||
|
:type foo: :class:`some.addon.Class`
|
||||||
|
|
||||||
|
Likewise, mentions of other methods (including within the same
|
||||||
|
class), modules or types should be cross-referenced.
|
||||||
|
|
||||||
|
* Classes should likewise be documented using their own docstring, and
|
||||||
|
should include the documentation of their construction (``__init__``
|
||||||
|
and ``__new__``), using the `info fields`_ as well.
|
||||||
|
|
||||||
|
* Attributes (class and instance) should be documented in their
|
||||||
|
class's docstrin via the ``.. attribute::`` directiveg, following
|
||||||
|
the class's own documentation.
|
||||||
|
|
||||||
|
* The relation between modules and module-level attributes is similar:
|
||||||
|
modules should be documented in their own docstring, public module
|
||||||
|
attributes should be documented in the module's docstring using the
|
||||||
|
``.. data::`` directive.
|
||||||
|
|
||||||
|
Javascript API documentation
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Javascript API documentation uses JsDoc_, a javascript documentation
|
||||||
|
toolkit with a syntax similar to (and inspired by) JavaDoc's.
|
||||||
|
|
||||||
|
Due to limitations of JsDoc, the coding patterns in OpenERP Web and
|
||||||
|
the Sphinx integration, there are a few peculiarities to be aware of
|
||||||
|
when writing javascript API documentation:
|
||||||
|
|
||||||
|
* Namespaces and classes *must* be explicitly marked up even if they
|
||||||
|
are not documented, or JsDoc will not understand what they are and
|
||||||
|
will not generate documentation for their content.
|
||||||
|
|
||||||
|
As a result, the bare minimum for a namespace is::
|
||||||
|
|
||||||
|
/** @namespace */
|
||||||
|
foo.bar.baz = {};
|
||||||
|
|
||||||
|
while for a class it is::
|
||||||
|
|
||||||
|
/** @class */
|
||||||
|
foo.bar.baz.Qux = [...]
|
||||||
|
|
||||||
|
* Because the OpenERP Web project uses `John Resig's Class
|
||||||
|
implementation`_ instead of direct prototypal inheritance [#]_,
|
||||||
|
JsDoc fails to infer class scopes (and constructors or super
|
||||||
|
classes, for that matter) and has to be told explicitly.
|
||||||
|
|
||||||
|
See :ref:`js-class-doc` for the complete rundown.
|
||||||
|
|
||||||
|
* Much like the JavaDoc, JsDoc does not include a full markup
|
||||||
|
language. Instead, comments are simply marked up in HTML.
|
||||||
|
|
||||||
|
This has a number of inconvenients:
|
||||||
|
|
||||||
|
* Complex documentation comments become nigh-unreadable to read in
|
||||||
|
text editors (as opposed to IDEs, which may handle rendering
|
||||||
|
documentation comments on the fly)
|
||||||
|
|
||||||
|
* Though cross-references are supported by JsDoc (via ``@link`` and
|
||||||
|
``@see``), they only work within the JsDoc
|
||||||
|
|
||||||
|
* More general impossibility to integrate correctly with Sphinx, and
|
||||||
|
e.g. reference JavaScript objects from a tutorial, or have all the
|
||||||
|
documentation live at the same place.
|
||||||
|
|
||||||
|
As a result, JsDoc comments should be marked up using RST, not
|
||||||
|
HTML. They may use Sphinx's cross-references as well.
|
||||||
|
|
||||||
|
.. _js-class-doc:
|
||||||
|
|
||||||
|
Documenting a Class
|
||||||
|
*******************
|
||||||
|
|
||||||
|
The first task when documenting a class using JsDoc is to *mark* that
|
||||||
|
class, so JsDoc knows it can be used to instantiate objects (and, more
|
||||||
|
importantly as far as it's concerned, should be documented with
|
||||||
|
methods and attributes and stuff).
|
||||||
|
|
||||||
|
This is generally done through the ``@class`` tag, but this tag has a
|
||||||
|
significant limitation: it "believes" the constructor and the class
|
||||||
|
are one and the same [#]_. This will work for constructor-less
|
||||||
|
classes, but because OpenERP Web uses Resig's class the constructor is
|
||||||
|
not the class itself but its ``init()`` method.
|
||||||
|
|
||||||
|
Because this pattern is common in modern javascript code bases, JsDoc
|
||||||
|
supports it: it is possible to mark an arbitrary instance method as
|
||||||
|
the *class specification* by using the ``@constructs`` tag.
|
||||||
|
|
||||||
|
.. warning:: ``@constructs`` is a class specification in and of
|
||||||
|
itself, it *completely replaces* the class documentation.
|
||||||
|
|
||||||
|
Using both a class documentation (even without ``@class`` itself)
|
||||||
|
and a constructor documentation is an *error* in JsDoc and will
|
||||||
|
result in incorrect behavior and broken documentation.
|
||||||
|
|
||||||
|
The second issue is that Resig's class uses an object literal to
|
||||||
|
specify instance methods, and because JsDoc does not know anything
|
||||||
|
about Resig's class, it does not know about the role of the object
|
||||||
|
literal.
|
||||||
|
|
||||||
|
As with constructors, though, JsDoc provides a pluggable way to tell
|
||||||
|
it about methods: the ``@lends`` tag. It specifies that the object
|
||||||
|
literal "lends" its properties to the class being built.
|
||||||
|
|
||||||
|
``@lends`` must be specified right before the opening brace of the
|
||||||
|
object literal (between the opening paren of the ``#extend`` call and
|
||||||
|
the brace), and takes the full qualified name of the class being
|
||||||
|
created as a parameter, followed by the character ``#`` or by
|
||||||
|
``.prototype``. This latter part tells JsDoc these are instance
|
||||||
|
methods, not class (static) methods..
|
||||||
|
|
||||||
|
Finally, specifying a class's superclass is done through the
|
||||||
|
``@extends`` tag, which takes a fully qualified class name as a
|
||||||
|
parameter.
|
||||||
|
|
||||||
|
Here are a class without a constructor, and a class with one, so that
|
||||||
|
everything is clear (these are straight from the OpenERP Web source,
|
||||||
|
with the descriptions and irrelevant atttributes stripped):
|
||||||
|
|
||||||
|
.. code-block:: javascript
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <Insert description here, not below>
|
||||||
|
*
|
||||||
|
* @class
|
||||||
|
* @extends openerp.base.search.Field
|
||||||
|
*/
|
||||||
|
openerp.base.search.CharField = openerp.base.search.Field.extend(
|
||||||
|
/** @lends openerp.base.search.CharField# */ {
|
||||||
|
// methods here
|
||||||
|
});
|
||||||
|
|
||||||
|
.. code-block:: javascript
|
||||||
|
|
||||||
|
|
||||||
|
openerp.base.search.Widget = openerp.base.Controller.extend(
|
||||||
|
/** @lends openerp.base.search.Widget# */{
|
||||||
|
/**
|
||||||
|
* <Insert description here, not below>
|
||||||
|
*
|
||||||
|
* @constructs
|
||||||
|
* @extends openerp.base.Controller
|
||||||
|
*
|
||||||
|
* @param view the ancestor view of this widget
|
||||||
|
*/
|
||||||
|
init: function (view) {
|
||||||
|
// construction of the instance
|
||||||
|
},
|
||||||
|
// bunch of other methods
|
||||||
|
});
|
||||||
|
|
||||||
OpenERP Web over time
|
OpenERP Web over time
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
|
@ -30,3 +217,35 @@ Roadmap
|
||||||
|
|
||||||
Release notes
|
Release notes
|
||||||
+++++++++++++
|
+++++++++++++
|
||||||
|
|
||||||
|
.. [#] because Python is the default domain, the ``py:`` markup prefix
|
||||||
|
is optional and should be left out.
|
||||||
|
|
||||||
|
.. [#] Resig's Class still uses prototypes under the hood, it doesn't
|
||||||
|
reimplement its own object system although it does add several
|
||||||
|
helpers such as the ``_super()`` instance method.
|
||||||
|
|
||||||
|
.. [#] Which is the case in normal Javascript semantics. Likewise, the
|
||||||
|
``.prototype`` / ``#`` pattern we will see later on is due to
|
||||||
|
JsDoc defaulting to the only behavior it can rely on: "normal"
|
||||||
|
Javascript prototype-based type creation.
|
||||||
|
|
||||||
|
.. _reStructuredText:
|
||||||
|
http://docutils.sourceforge.net/rst.html
|
||||||
|
.. _Sphinx:
|
||||||
|
http://sphinx.pocoo.org/index.html
|
||||||
|
.. _Sphinx's extensions:
|
||||||
|
http://sphinx.pocoo.org/markup/index.html
|
||||||
|
.. _Python domain:
|
||||||
|
http://sphinx.pocoo.org/domains.html#the-python-domain
|
||||||
|
.. _info fields:
|
||||||
|
http://sphinx.pocoo.org/domains.html#info-field-lists
|
||||||
|
.. _autodoc:
|
||||||
|
http://sphinx.pocoo.org/ext/autodoc.html?highlight=autodoc#sphinx.ext.autodoc
|
||||||
|
.. _cross-references:
|
||||||
|
http://sphinx.pocoo.org/markup/inline.html#xref-syntax
|
||||||
|
.. _JsDoc:
|
||||||
|
.. _JsDoc Toolkit:
|
||||||
|
http://code.google.com/p/jsdoc-toolkit/
|
||||||
|
.. _John Resig's Class implementation:
|
||||||
|
http://ejohn.org/blog/simple-javascript-inheritance/
|
||||||
|
|
Loading…
Reference in New Issue