1 The OpenERP Web open-source project
2 ===================================
13 Source code repository
14 ++++++++++++++++++++++
19 Coding issues and coding conventions
20 ++++++++++++++++++++++++++++++++++++
25 These are a number of guidelines for javascript code. More than coding
26 conventions, these are warnings against potentially harmful or sub-par
29 Ideally, you should be able to configure your editor or IDE to warn you against
30 these kinds of issues.
32 Use ``var`` for *all* declarations
33 **********************************
35 In javascript (as opposed to Python), assigning to a variable which does not
36 already exist and is not explicitly declared (via ``var``) will implicitly
37 create a global variable. This is bad for a number of reasons:
39 * It leaks information outside function scopes
40 * It keeps memory of previous run, with potentially buggy behaviors
41 * It may conflict with other functions with the same issue
42 * It makes code harder to statically check (via e.g. IDE inspectors)
45 It is perfectly possible to use ``var`` in ``for`` loops:
47 .. code-block:: javascript
49 for (var i = 0; i < some_array.length; ++i) {
55 All local *and global* variables should be declared via ``var``.
57 .. note:: generally speaking, you should not need globals in OpenERP Web: you
58 can just declare a variable local to your top-level function. This
59 way, if your widget/addon is instantiated several times on the same
60 page (because it's used in embedded mode) each instance will have its
61 own internal but global-to-its-objects data.
63 Do not leave trailing commas in object literals
64 ***********************************************
66 While it is legal to leave trailing commas in Python dictionaries, e.g.
68 .. code-block:: python
75 and it's valid in ECMAScript 5 and most browsers support it in Javascript, you
76 should *never* use trailing commas in Javascript object literals:
78 * Internet Explorer does *not* support trailing commas (at least until and
79 including Internet Explorer 8), and trailing comma will cause hard-to-debug
82 * JSON does not accept trailing comma (it is a syntax error), and using them
83 in object literals puts you at risks of using them in literal JSON strings
84 as well (though there are few reasons to write JSON by hand)
86 *Never* use ``for … in`` to iterate on arrays
87 *********************************************
89 :ref:`Iterating over an object with for…in is a bit tricky already
90 <for-in-iteration>`, it is far more complex than in Python (where it Just
91 Works™) due to the interaction of various Javascript features, but to iterate
92 on arrays it becomes downright deadly and errorneous: ``for…in`` really
93 iterates over an *object*'s *properties*.
95 With an array, this has the following consequences:
97 * It does not necessarily iterate in numerical order, nor does it iterate in
98 any kind of set order. The order is implementation-dependent and may vary
99 from one run to the next depending on a number of reasons and implementation
101 * If properties are added to an array, to ``Array.prototype`` or to
102 ``Object.prototype`` (the latter two should not happen in well-behaved
103 javascript code, but you never know...) those properties *will* be iterated
104 over by ``for…in``. While ``Object.hasOwnProperty`` will guard against
105 iterating prototype properties, they will not guard against properties set
106 on the array instance itself (as memoizers for instance).
108 Note that this includes setting negative keys on arrays.
110 For this reason, ``for…in`` should **never** be used on array objects. Instead,
111 you should use either a normal ``for`` or (even better, unless you have
112 profiled the code and found a hotspot) one of Underscore's array iteration
113 methods (`_.each`_, `_.map`_, `_.filter`_, etc...).
115 Underscore is guaranteed to be bundled and available in OpenERP Web scopes.
117 .. _for-in-iteration:
119 Use ``hasOwnProperty`` when iterating on an object with ``for … in``
120 ********************************************************************
122 ``for…in`` is Javascript's built-in facility for iterating over and object's
125 `It is also fairly tricky to use`_: it iterates over *all* non-builtin
126 properties of your objects [#]_, which includes methods of an object's class.
128 As a result, when iterating over an object with ``for…in`` the first line of
129 the body *should* generally be a call to `Object.hasOwnProperty`_. This call
130 will check whether the property was set directly on the object or comes from
133 .. code-block:: javascript
136 if (!ob.hasOwnProperty(key)) {
137 // comes from ob's class
143 Since properties can be added directly to e.g. ``Object.prototype`` (even
144 though it's usually considered bad style), you should not assume you ever know
145 which properties ``for…in`` is going to iterate over.
147 An alternative is to use Underscore's iteration methods, which generally work
148 over objects as well as arrays:
152 .. code-block:: javascript
154 for (var key in ob) {
155 if (!ob.hasOwnProperty(key)) { continue; }
157 // Do stuff with key and value
162 .. code-block:: javascript
164 _.each(ob, function (value, key) {
165 // do stuff with key and value
168 and not worry about the details of the iteration: underscore should do the
169 right thing for you on its own [#]_.
171 Writing documentation
172 +++++++++++++++++++++
174 The OpenERP Web project documentation uses Sphinx_ for the literate
175 documentation (this document for instance), the development guides
176 (for Python and Javascript alike) and the Python API documentation
179 For the Javascript API, documentation should be written using the
182 Guides and main documentation
183 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
185 The meat and most important part of all documentation. Should be
186 written in plain English, using reStructuredText_ and taking advantage
187 of `Sphinx's extensions`_, especially `cross-references`_.
189 Python API Documentation
190 ~~~~~~~~~~~~~~~~~~~~~~~~
192 All public objects in Python code should have a docstring written in
193 RST, using Sphinx's `Python domain`_ [#]_:
195 * Functions and methods documentation should be in their own
196 docstring, using Sphinx's `info fields`_
198 For parameters types, built-in and stdlib types should be using the
201 .. code-block:: restructuredtext
203 :param dict foo: what the purpose of foo is
205 unless a more extensive explanation needs to be given (e.g. the
206 specification that the input should be a list of 3-tuple needs to
207 use ``:type:`` even though all types involved are built-ins). Any
208 other type should be specified in full using the ``:type:`` field
210 .. code-block:: restructuredtext
212 :param foo: what the purpose of foo is
213 :type foo: some.addon.Class
215 Mentions of other methods (including within the same class), modules
216 or types in descriptions (of anything, including parameters) should
219 * Classes should likewise be documented using their own docstring, and
220 should include the documentation of their construction (``__init__``
221 and ``__new__``), using the `info fields`_ as well.
223 * Attributes (class and instance) should be documented in their
224 class's docstring via the ``.. attribute::`` directive, following
225 the class's own documentation.
227 * The relation between modules and module-level attributes is similar:
228 modules should be documented in their own docstring, public module
229 attributes should be documented in the module's docstring using the
230 ``.. data::`` directive.
232 Javascript API documentation
233 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
235 Javascript API documentation uses JsDoc_, a javascript documentation
236 toolkit with a syntax similar to (and inspired by) JavaDoc's.
238 Due to limitations of JsDoc, the coding patterns in OpenERP Web and
239 the Sphinx integration, there are a few peculiarities to be aware of
240 when writing javascript API documentation:
242 * Namespaces and classes *must* be explicitly marked up even if they
243 are not documented, or JsDoc will not understand what they are and
244 will not generate documentation for their content.
246 As a result, the bare minimum for a namespace is:
248 .. code-block:: javascript
253 while for a class it is:
255 .. code-block:: javascript
258 foo.bar.baz.Qux = [...]
260 * Because the OpenERP Web project uses `John Resig's Class
261 implementation`_ instead of direct prototypal inheritance [#]_,
262 JsDoc fails to infer class scopes (and constructors or super
263 classes, for that matter) and has to be told explicitly.
265 See :ref:`js-class-doc` for the complete rundown.
267 * Much like the JavaDoc, JsDoc does not include a full markup
268 language. Instead, comments are simply marked up in HTML.
270 This has a number of inconvenients:
272 * Complex documentation comments become nigh-unreadable to read in
273 text editors (as opposed to IDEs, which may handle rendering
274 documentation comments on the fly)
276 * Though cross-references are supported by JsDoc (via ``@link`` and
277 ``@see``), they only work within the JsDoc
279 * More general impossibility to integrate correctly with Sphinx, and
280 e.g. reference JavaScript objects from a tutorial, or have all the
281 documentation live at the same place.
283 As a result, JsDoc comments should be marked up using RST, not
284 HTML. They may use Sphinx's cross-references as well.
291 The first task when documenting a class using JsDoc is to *mark* that
292 class, so JsDoc knows it can be used to instantiate objects (and, more
293 importantly as far as it's concerned, should be documented with
294 methods and attributes and stuff).
296 This is generally done through the ``@class`` tag, but this tag has a
297 significant limitation: it "believes" the constructor and the class
298 are one and the same [#]_. This will work for constructor-less
299 classes, but because OpenERP Web uses Resig's class the constructor is
300 not the class itself but its ``init()`` method.
302 Because this pattern is common in modern javascript code bases, JsDoc
303 supports it: it is possible to mark an arbitrary instance method as
304 the *class specification* by using the ``@constructs`` tag.
306 .. warning:: ``@constructs`` is a class specification in and of
307 itself, it *completely replaces* the class documentation.
309 Using both a class documentation (even without ``@class`` itself)
310 and a constructor documentation is an *error* in JsDoc and will
311 result in incorrect behavior and broken documentation.
313 The second issue is that Resig's class uses an object literal to
314 specify instance methods, and because JsDoc does not know anything
315 about Resig's class, it does not know about the role of the object
318 As with constructors, though, JsDoc provides a pluggable way to tell
319 it about methods: the ``@lends`` tag. It specifies that the object
320 literal "lends" its properties to the class being built.
322 ``@lends`` must be specified right before the opening brace of the
323 object literal (between the opening paren of the ``#extend`` call and
324 the brace), and takes the full qualified name of the class being
325 created as a parameter, followed by the character ``#`` or by
326 ``.prototype``. This latter part tells JsDoc these are instance
327 methods, not class (static) methods..
329 Finally, specifying a class's superclass is done through the
330 ``@extends`` tag, which takes a fully qualified class name as a
333 Here are a class without a constructor, and a class with one, so that
334 everything is clear (these are straight from the OpenERP Web source,
335 with the descriptions and irrelevant atttributes stripped):
337 .. code-block:: javascript
340 * <Insert description here, not below>
343 * @extends openerp.base.search.Field
345 openerp.base.search.CharField = openerp.base.search.Field.extend(
346 /** @lends openerp.base.search.CharField# */ {
350 .. code-block:: javascript
352 openerp.base.search.Widget = openerp.base.Controller.extend(
353 /** @lends openerp.base.search.Widget# */{
355 * <Insert description here, not below>
358 * @extends openerp.base.Controller
360 * @param view the ancestor view of this widget
362 init: function (view) {
363 // construction of the instance
365 // bunch of other methods
368 OpenERP Web over time
369 ---------------------
374 OpenSUSE packaging: http://blog.lowkster.com/2011/04/packaging-python-packages-in-opensuse.html
382 .. [#] More precisely, it iterates over all *enumerable* properties. It just
383 happens that built-in properties (such as ``String.indexOf`` or
384 ``Object.toString``) are set to non-enumerable.
386 The enumerability of a property can be checked using
387 `Object.propertyIsEnumeable`_.
389 Before ECMAScript 5, it was not possible for user-defined properties
390 to be non-enumerable in a portable manner. ECMAScript 5 introduced
391 `Object.defineProperty`_ which lets user code create non-enumerable
392 properties (and more, read-only properties for instance, or implicit
393 getters and setters). However, support for these is not fully complete
394 at this point, and they are not being used in OpenERP Web code anyway.
396 .. [#] While using underscore is generally the preferred method (simpler,
397 more reliable and easier to write than a *correct* ``for…in``
398 iteration), it is also probably slower (due to the overhead of
399 calling a bunch of functions).
401 As a result, if you profile some code and find out that an underscore
402 method adds unacceptable overhead in a tight loop, you may want to
403 replace it with a ``for…in`` (or a regular ``for`` statement for
406 .. [#] Because Python is the default domain, the ``py:`` markup prefix
407 is optional and should be left out.
409 .. [#] Resig's Class still uses prototypes under the hood, it doesn't
410 reimplement its own object system although it does add several
411 helpers such as the ``_super()`` instance method.
413 .. [#] Which is the case in normal Javascript semantics. Likewise, the
414 ``.prototype`` / ``#`` pattern we will see later on is due to
415 JsDoc defaulting to the only behavior it can rely on: "normal"
416 Javascript prototype-based type creation.
418 .. _reStructuredText:
419 http://docutils.sourceforge.net/rst.html
421 http://sphinx.pocoo.org/index.html
422 .. _Sphinx's extensions:
423 http://sphinx.pocoo.org/markup/index.html
425 http://sphinx.pocoo.org/domains.html#the-python-domain
427 http://sphinx.pocoo.org/domains.html#info-field-lists
429 http://sphinx.pocoo.org/ext/autodoc.html
430 ?highlight=autodoc#sphinx.ext.autodoc
431 .. _cross-references:
432 http://sphinx.pocoo.org/markup/inline.html#xref-syntax
435 http://code.google.com/p/jsdoc-toolkit/
436 .. _John Resig's Class implementation:
437 http://ejohn.org/blog/simple-javascript-inheritance/
439 http://documentcloud.github.com/underscore/#each
441 http://documentcloud.github.com/underscore/#map
443 http://documentcloud.github.com/underscore/#select
444 .. _It is also fairly tricky to use:
445 https://developer.mozilla.org/en/JavaScript/Reference/Statements/for...in#Description
446 .. _Object.propertyIsEnumeable:
447 https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/propertyIsEnumerable
448 .. _Object.defineProperty:
449 https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty
450 .. _Object.hasOwnProperty:
451 https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/hasOwnProperty