[REM] old doc
bzr revid: xmo@openerp.com-20121002134604-w6xbkg7sqitd96db
This commit is contained in:
parent
c28dfb19e1
commit
c8dead4f60
449
doc/addons.rst
449
doc/addons.rst
|
@ -1,449 +0,0 @@
|
|||
Developing OpenERP Web Addons
|
||||
=============================
|
||||
|
||||
An OpenERP Web addon is simply a Python package with an openerp
|
||||
descriptor (a ``__openerp__.py`` file) which follows a few structural
|
||||
and namespacing rules.
|
||||
|
||||
Structure
|
||||
---------
|
||||
|
||||
.. literalinclude:: addon-structure.txt
|
||||
|
||||
``__openerp__.py``
|
||||
The addon's descriptor, contains the following information:
|
||||
|
||||
``name: str``
|
||||
The addon name, in plain, readable english
|
||||
``version: str``
|
||||
The addon version, following `Semantic Versioning`_ rules
|
||||
``depends: [str]``
|
||||
A list of addons this addon needs to work correctly. ``base`` is
|
||||
an implied dependency if the list is empty.
|
||||
``css: [str]``
|
||||
An ordered list of CSS files this addon provides and needs. The
|
||||
file paths are relative to the addon's root. Because the Web
|
||||
Client *may* perform concatenations and other various
|
||||
optimizations on CSS files, the order is important.
|
||||
``js: [str]``
|
||||
An ordered list of Javascript files this addon provides and needs
|
||||
(including dependencies files). As with CSS files, the order is
|
||||
important as the Web Client *may* perform contatenations and
|
||||
minimizations of files.
|
||||
``active: bool``
|
||||
Whether this addon should be enabled by default any time it is
|
||||
found, or whether it will be enabled through other means (on a
|
||||
by-need or by-installation basis for instance).
|
||||
|
||||
``controllers/``
|
||||
All of the Python controllers and JSON-RPC endpoints.
|
||||
|
||||
``static/``
|
||||
The static files directory, may be served via a separate web server.
|
||||
|
||||
``static/lib/``
|
||||
Third-party libraries used by the addon.
|
||||
|
||||
``static/src/{css,js,img,xml}``
|
||||
Location for (respectively) the addon's static CSS files, its JS
|
||||
files, its various image resources as well as the template files
|
||||
|
||||
``static/test``
|
||||
Javascript tests files
|
||||
|
||||
``test/``
|
||||
The directories in which all tests for the addon are located.
|
||||
|
||||
Some of these are guidelines (and not enforced by code), but it's
|
||||
suggested that these be followed. Code which does not fit into these
|
||||
categories can go wherever deemed suitable.
|
||||
|
||||
Namespacing
|
||||
-----------
|
||||
|
||||
Python
|
||||
++++++
|
||||
|
||||
Because addons are also Python packages, they're inherently namespaced
|
||||
and nothing special needs to be done on that front.
|
||||
|
||||
JavaScript
|
||||
++++++++++
|
||||
|
||||
The JavaScript side of an addon has to live in the namespace
|
||||
``openerp.$addon_name``. For instance, everything created by the addon
|
||||
``base`` lives in ``openerp.base``.
|
||||
|
||||
The root namespace of the addon is a function which takes a single
|
||||
parameter ``openerp``, which is an OpenERP client instance. Objects
|
||||
(as well as functions, registry instances, etc...) should be added on
|
||||
the correct namespace on that object.
|
||||
|
||||
The root function will be called by the OpenERP Web client when
|
||||
initializing the addon.
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
// root namespace of the openerp.example addon
|
||||
/** @namespace */
|
||||
openerp.example = function (openerp) {
|
||||
// basic initialization code (e.g. templates loading)
|
||||
openerp.example.SomeClass = openerp.base.Class.extend(
|
||||
/** @lends openerp.example.SomeClass# */{
|
||||
/**
|
||||
* Description for SomeClass's constructor here
|
||||
*
|
||||
* @constructs
|
||||
*/
|
||||
init: function () {
|
||||
// SomeClass initialization code
|
||||
}
|
||||
// rest of SomeClass
|
||||
});
|
||||
|
||||
// access an object in an other addon namespace to replace it
|
||||
openerp.base.SearchView = openerp.base.SearchView.extend({
|
||||
init: function () {
|
||||
this._super.apply(this, arguments);
|
||||
console.log('Search view initialized');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Creating new standard roles
|
||||
---------------------------
|
||||
|
||||
Views
|
||||
+++++
|
||||
|
||||
Views are the standard high-level component in OpenERP. A view type corresponds
|
||||
to a way to display a set of data (coming from an OpenERP model).
|
||||
|
||||
In OpenERP Web, views are standard objects registered against a dedicated
|
||||
object registry, so the :js:class:`~openerp.base.ViewManager` knows where to
|
||||
find and how to call them.
|
||||
|
||||
Although not mandatory, it is recommended that views inherit from
|
||||
:js:class:`openerp.base.View`, which provides a view useful services to its
|
||||
children.
|
||||
|
||||
Registering a view
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This is the first task to perform when creating a view, and the simplest by
|
||||
far: simply call ``openerp.base.views.add(name, object_path)`` to register
|
||||
the object of path ``object_path`` as the view for the view name ``name``.
|
||||
|
||||
The view name is the name you gave to your new view in the OpenERP server.
|
||||
|
||||
From that point onwards, OpenERP Web will be able to find your object and
|
||||
instantiate it.
|
||||
|
||||
Standard view behaviors
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In the normal OpenERP Web flow, views have to implement a number of methods so
|
||||
view managers can correctly communicate with them:
|
||||
|
||||
``start()``
|
||||
This method will always be called after creating the view (via its
|
||||
constructor), but not necessarily immediately.
|
||||
|
||||
It is called with no arguments and should handle the heavy setup work,
|
||||
including remote call (to load the view's setup data from the server via
|
||||
e.g. ``fields_view_get``, for instance).
|
||||
|
||||
``start`` should return a `promise object`_ which *must* be resolved when
|
||||
the view's setup is completed. This promise is used by view managers to
|
||||
know when they can start interacting with the view.
|
||||
|
||||
``do_hide()``
|
||||
Called by the view manager when it wants to replace this view by an other
|
||||
one, but wants to keep this view around to re-activate it later.
|
||||
|
||||
Should put the view in some sort of hibernation mode, and *must* hide its
|
||||
DOM elements.
|
||||
|
||||
``do_show()``
|
||||
Called when the view manager wants to re-display the view after having
|
||||
hidden it. The view should refresh its data display upon receiving this
|
||||
notification
|
||||
|
||||
``do_search(domain: Array, context: Object, group_by: Array)``
|
||||
If the view is searchable, this method is called to notify it of a search
|
||||
against it.
|
||||
|
||||
It should use the provided query data to perform a search and refresh its
|
||||
internal content (and display).
|
||||
|
||||
All views are searchable by default, but they can be made non-searchable
|
||||
by setting the property ``searchable`` to ``false``.
|
||||
|
||||
This can be done either on the view class itself (at the same level as
|
||||
defining e.g. the ``start`` method) or at the instance level (in the
|
||||
class's ``init``), though you should generally set it on the class.
|
||||
|
||||
Frequent development tasks
|
||||
--------------------------
|
||||
|
||||
There are a number of tasks which OpenERP Web developers do or will need to
|
||||
perform quite regularly. To make these easier, we have written a few guides
|
||||
to help you get started:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
guides/client-action
|
||||
guides/sidebar-protocol
|
||||
|
||||
Translations
|
||||
------------
|
||||
|
||||
OpenERP Web should provide most of the tools needed to correctly translate your
|
||||
addons via the tool of your choice (OpenERP itself uses `Launchpad's own
|
||||
translation tool`_.
|
||||
|
||||
Making strings translatable
|
||||
+++++++++++++++++++++++++++
|
||||
|
||||
QWeb
|
||||
~~~~
|
||||
|
||||
QWeb automatically marks all text nodes (any text which is not in an XML
|
||||
attribute and not part of an XML tag) as translatable, and handles the
|
||||
replacement for you. There is nothing special to do to mark template text as
|
||||
translatable
|
||||
|
||||
JavaScript
|
||||
~~~~~~~~~~
|
||||
|
||||
OpenERP Web provides two functions to translate human-readable strings in
|
||||
javascript code. These functions should be "imported" in your module by
|
||||
aliasing them to their bare name:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
var _t = openerp.web._t,
|
||||
_tl = openerp.web._tl;
|
||||
|
||||
importing those functions under any other name is not guaranteed to work.
|
||||
|
||||
.. note:: only import them if necessary, and only the necessary one(s), no need
|
||||
to clutter your module's namespace for nothing
|
||||
|
||||
.. js:function:: openerp.web._t(s)
|
||||
|
||||
Base translation function, eager, works much like :manpage:`gettext(3)`
|
||||
|
||||
:type s: String
|
||||
:rtype: String
|
||||
|
||||
.. js:function:: openerp.web._lt(s)
|
||||
|
||||
Lazy equivalent to :js:func:`~openerp.web._t`, this function will postpone
|
||||
fetching the translation to its argument until the last possible moment.
|
||||
|
||||
To use in contexts evaluated before the translation database can be
|
||||
fetched, usually your module's toplevel and the attributes of classes
|
||||
defined in it (class attributes, not instance attributes set in the
|
||||
constructor).
|
||||
|
||||
:type s: String
|
||||
:rtype: LazyString
|
||||
|
||||
Text formatting & translations
|
||||
""""""""""""""""""""""""""""""
|
||||
|
||||
A difficulty when translating is integrating data (from the code) into the
|
||||
translated string. In OpenERP Web addons, this should be done by wrapping the
|
||||
text to translate in an :manpage:`sprintf(3)` call. For OpenERP Web,
|
||||
:manpage:`sprintf(3)` is provided by `underscore.string
|
||||
<http://epeli.github.com/underscore.string/>`_.
|
||||
|
||||
As much as possible, you should use the "named argument" form of sprintf:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
var translated_string = _.str.sprintf(
|
||||
_t("[%(first_record)d to %(last_record)d] of %(records_count)d"), {
|
||||
first_record: first + 1,
|
||||
last_record: last,
|
||||
records_count: total
|
||||
}));
|
||||
|
||||
named arguments make the string to translate much clearer for translators, and
|
||||
allows them to "move" sections around based on the requirements of their
|
||||
language (not all language order text like english).
|
||||
|
||||
Named arguments are specified using the following pattern: ``%($name)$type``
|
||||
where
|
||||
|
||||
``$name``
|
||||
the name of the argument, this is the key in the object/dictionary provided
|
||||
as second parameter to ``sprintf``
|
||||
``$type``
|
||||
a type/format specifier, `see the list for all possible types
|
||||
<http://www.diveintojavascript.com/projects/javascript-sprintf>`_.
|
||||
|
||||
.. note:: positional arguments are acceptable if the translated string has
|
||||
*a single* argument and its content is easy to guess from the text
|
||||
around it. Named arguments should still be preferred.
|
||||
|
||||
.. warning:: you should *never* use string concatenation as it robs the
|
||||
translator of context and make result in a completely incorrect
|
||||
translation
|
||||
|
||||
Extracting strings
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. program:: gen_translations.sh
|
||||
|
||||
Once strings have been marked for translation, they need to be extracted into
|
||||
:abbr:`POT (Portable Object Template)` files, from which most translation tools
|
||||
can build a database.
|
||||
|
||||
This can be done via the provided :program:`gen_translations.sh`.
|
||||
|
||||
It can be called either as :option:`gen_translations.sh -a` or by providing
|
||||
two parameters, a path to the addons and the complete path in which to put the
|
||||
extracted POT file.
|
||||
|
||||
.. option:: -a
|
||||
|
||||
Extracts translations from all standard OpenERP Web addons (addons bundled
|
||||
with OpenERP Web itself) and puts the extracted templates into the right
|
||||
directory for `Rosetta`_ to handle them
|
||||
|
||||
Utility behaviors
|
||||
-----------------
|
||||
|
||||
JavaScript
|
||||
++++++++++
|
||||
|
||||
* All javascript objects inheriting from
|
||||
:js:class:`openerp.base.BasicConroller` will have all methods
|
||||
starting with ``on_`` or ``do_`` bound to their ``this``. This means
|
||||
they don't have to be manually bound (via ``_.bind`` or ``$.proxy``)
|
||||
in order to be useable as bound event handlers (event handlers
|
||||
keeping their object as ``this`` rather than taking whatever
|
||||
``this`` object they were called with).
|
||||
|
||||
Beware that this is only valid for methods starting with ``do_`` and
|
||||
``on_``, any other method will have to be bound manually.
|
||||
|
||||
.. _addons-testing:
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
||||
Python
|
||||
++++++
|
||||
|
||||
OpenERP Web uses unittest2_ for its testing needs. We selected
|
||||
unittest2 rather than unittest_ for the following reasons:
|
||||
|
||||
* autodiscovery_ (similar to nose, via the ``unit2``
|
||||
CLI utility) and `pluggable test discovery`_.
|
||||
|
||||
* `new and improved assertions`_ (with improvements in type-specific
|
||||
inequality reportings) including `pluggable custom types equality
|
||||
assertions`_
|
||||
|
||||
* neveral new APIs, most notably `assertRaises context manager`_,
|
||||
`cleanup function registration`_, `test skipping`_ and `class- and
|
||||
module-level setup and teardown`_
|
||||
|
||||
* finally, unittest2 is a backport of Python 3's unittest. We might as
|
||||
well get used to it.
|
||||
|
||||
To run tests on addons (from the root directory of OpenERP Web) is as
|
||||
simple as typing ``PYTHONPATH=. unit2 discover -s addons`` [#]_. To
|
||||
test an addon which does not live in the ``addons`` directory, simply
|
||||
replace ``addons`` by the directory in which your own addon lives.
|
||||
|
||||
.. note:: unittest2 is entirely compatible with nose_ (or the
|
||||
other way around). If you want to use nose as your test
|
||||
runner (due to its addons for instance) you can simply install it
|
||||
and run ``nosetests addons`` instead of the ``unit2`` command,
|
||||
the result should be exactly the same.
|
||||
|
||||
Python
|
||||
++++++
|
||||
|
||||
.. autoclass:: web.common.session.OpenERPSession
|
||||
:members:
|
||||
|
||||
.. autoclass:: web.common.openerplib.main.Model
|
||||
:members:
|
||||
|
||||
* Addons lifecycle (loading, execution, events, ...)
|
||||
|
||||
* Python-side
|
||||
* JS-side
|
||||
|
||||
* Handling static files
|
||||
* Overridding a Python controller (object?)
|
||||
* Overridding a Javascript controller (object?)
|
||||
* Extending templates
|
||||
.. how do you handle deploying static files via e.g. a separate lighttpd?
|
||||
* Python public APIs
|
||||
* QWeb templates description?
|
||||
* OpenERP Web modules (from OpenERP modules)
|
||||
|
||||
.. [#] the ``-s`` parameter tells ``unit2`` to start trying to
|
||||
find tests in the provided directory (here we're testing
|
||||
addons). However a side-effect of that is to set the
|
||||
``PYTHONPATH`` there as well, so it will fail to find (and
|
||||
import) ``openerpweb``.
|
||||
|
||||
The ``-t`` parameter lets us set the ``PYTHONPATH``
|
||||
independently, but it doesn't accept multiple values and here
|
||||
we really want to have both ``.`` and ``addons`` on the
|
||||
``PYTHONPATH``.
|
||||
|
||||
The solution is to set the ``PYTHONPATH`` to ``.`` on start,
|
||||
and the ``start-directory`` to ``addons``. This results in a
|
||||
correct ``PYTHONPATH`` within ``unit2``.
|
||||
|
||||
.. _unittest:
|
||||
http://docs.python.org/library/unittest.html
|
||||
|
||||
.. _unittest2:
|
||||
http://www.voidspace.org.uk/python/articles/unittest2.shtml
|
||||
|
||||
.. _autodiscovery:
|
||||
http://www.voidspace.org.uk/python/articles/unittest2.shtml#test-discovery
|
||||
|
||||
.. _pluggable test discovery:
|
||||
http://www.voidspace.org.uk/python/articles/unittest2.shtml#load-tests
|
||||
|
||||
.. _new and improved assertions:
|
||||
http://www.voidspace.org.uk/python/articles/unittest2.shtml#new-assert-methods
|
||||
|
||||
.. _pluggable custom types equality assertions:
|
||||
http://www.voidspace.org.uk/python/articles/unittest2.shtml#add-new-type-specific-functions
|
||||
|
||||
.. _assertRaises context manager:
|
||||
http://www.voidspace.org.uk/python/articles/unittest2.shtml#assertraises
|
||||
|
||||
.. _cleanup function registration:
|
||||
http://www.voidspace.org.uk/python/articles/unittest2.shtml#cleanup-functions-with-addcleanup
|
||||
|
||||
.. _test skipping:
|
||||
http://www.voidspace.org.uk/python/articles/unittest2.shtml#test-skipping
|
||||
|
||||
.. _class- and module-level setup and teardown:
|
||||
http://www.voidspace.org.uk/python/articles/unittest2.shtml#class-and-module-level-fixtures
|
||||
|
||||
.. _Semantic Versioning:
|
||||
http://semver.org/
|
||||
|
||||
.. _nose:
|
||||
http://somethingaboutorange.com/mrl/projects/nose/1.0.0/
|
||||
|
||||
.. _promise object:
|
||||
http://api.jquery.com/deferred.promise/
|
||||
|
||||
.. _Rosetta:
|
||||
.. _Launchpad's own translation tool:
|
||||
https://help.launchpad.net/Translations
|
|
@ -1,424 +0,0 @@
|
|||
OpenERP Web Core and standard addons
|
||||
====================================
|
||||
|
||||
* General organization and core ideas (design philosophies)
|
||||
* Internal documentation, autodoc, Python and JS domains
|
||||
* QWeb code documentation/description
|
||||
* Documentation of the OpenERP APIs and choices taken based on that?
|
||||
* Style guide and coding conventions (PEP8? More)
|
||||
* Test frameworks in JS?
|
||||
|
||||
Standard Views
|
||||
--------------
|
||||
|
||||
Search View
|
||||
+++++++++++
|
||||
|
||||
The OpenERP search view really is a sub-view, used in support of views
|
||||
acting on collections of records (list view or graph view, for
|
||||
instance).
|
||||
|
||||
Its main goal is to collect information from its widgets (themselves
|
||||
collecting information from the users) and make those available to the
|
||||
rest of the client.
|
||||
|
||||
The search view's root is :js:class:`~openerp.base.SearchView`. This
|
||||
object should never need to be created or managed directly, its
|
||||
lifecycle should be driven by the
|
||||
:js:class:`~openerp.base.ViewManager`.
|
||||
|
||||
.. TODO: insert SearchView constructor here
|
||||
|
||||
The search view defines a number of internal and external protocols to
|
||||
communicate with the objects around and within it. Most of these
|
||||
protocols are informal, and types available for inheritance are more
|
||||
mixins than mandatory.
|
||||
|
||||
Events
|
||||
""""""
|
||||
|
||||
``on_loaded``
|
||||
|
||||
.. TODO: method openerp.base.SearchView.on_loaded
|
||||
|
||||
Fires when the search view receives its view data (the result of
|
||||
``fields_view_get``). Hooking up before the event allows for
|
||||
altering view data before it can be used.
|
||||
|
||||
By the time ``on_loaded`` is done, the search view is guaranteed to
|
||||
be fully set up and ready to use.
|
||||
|
||||
``on_search``
|
||||
|
||||
.. TODO: method openerp.base.SearchView.on_search
|
||||
|
||||
Event triggered after a user asked for a search. The search view
|
||||
fires this event after collecting all input data (contexts, domains
|
||||
and group_by contexts). Note that the search view does *not* merge
|
||||
those (or otherwise evaluate them), they are returned as provided by
|
||||
the various inputs within the view.
|
||||
|
||||
``on_clear``
|
||||
|
||||
.. TODO: method openerp.base.SearchView.on_clear
|
||||
|
||||
Triggered after a user asked for a form clearing.
|
||||
|
||||
Input management
|
||||
""""""""""""""""
|
||||
|
||||
An important concept in the search view is that of input. It is both
|
||||
an informal protocol and an abstract type that can be inherited from.
|
||||
|
||||
Inputs are widgets which can contain user data (a char widget for
|
||||
instance, or a selection box). They are capable of action and of
|
||||
reaction:
|
||||
|
||||
.. _views-search-registration:
|
||||
|
||||
``registration``
|
||||
|
||||
This is an input action. Inputs have to register themselves to the
|
||||
main view (which they receive as a constructor argument). This is
|
||||
performed by pushing themselves on the
|
||||
:js:attr:`openerp.base.SearchView.inputs` array.
|
||||
|
||||
``get_context``
|
||||
|
||||
An input reaction. When it needs to collect contexts, the view calls
|
||||
``get_context()`` on all its inputs.
|
||||
|
||||
Inputs can react in the following manners:
|
||||
|
||||
* Return a context (an object), this is the "normal" response if the
|
||||
input holds a value.
|
||||
|
||||
* Return a value that evaluates as false (generally ``null``). This
|
||||
value indicates the input does not contain any value and will not
|
||||
affect the results of the search.
|
||||
|
||||
* Raise :js:class:`openerp.base.search.Invalid` to indicate that it
|
||||
holds a value but this value can not be used in the search
|
||||
(because it is incorrectly formatted or nonsensical). Raising
|
||||
:js:class:`~openerp.base.search.Invalid` is guaranteed to cancel
|
||||
the search process.
|
||||
|
||||
:js:class:`~openerp.base.search.Invalid` takes three mandatory
|
||||
arguments: an identifier (a name for instance), the invalid value,
|
||||
and a validation message indicating the issue.
|
||||
|
||||
``get_domain``
|
||||
|
||||
The second input reaction, the possible behaviors of inputs are the
|
||||
same as for ``get_context``.
|
||||
|
||||
The :js:class:`openerp.base.search.Input` type implements registration
|
||||
on its own, but its implementations of ``get_context`` and
|
||||
``get_domain`` simply raise errors and *must* be overridden.
|
||||
|
||||
One last action is for filters, as an activation order has to be kept
|
||||
on them for some controls (to establish the correct grouping sequence,
|
||||
for instance).
|
||||
|
||||
To that end, filters can call
|
||||
:js:func:`openerp.base.Search.do_toggle_filter`, providing themselves
|
||||
as first argument.
|
||||
|
||||
Filters calling :js:func:`~openerp.base.Search.do_toggle_filter` also
|
||||
need to implement a method called
|
||||
:js:func:`~openerp.base.search.Filter.is_enabled`, which the search
|
||||
view will use to know the current status of the filter.
|
||||
|
||||
The search view automatically triggers a search after calls to
|
||||
:js:func:`~openerp.base.Search.do_toggle_filter`.
|
||||
|
||||
Life cycle
|
||||
""""""""""
|
||||
|
||||
The search view has a pretty simple and linear life cycle, in three main steps:
|
||||
|
||||
:js:class:`~openerp.base.SearchView.init`
|
||||
|
||||
Nothing interesting happens here
|
||||
|
||||
:js:func:`~openerp.base.SearchView.start`
|
||||
|
||||
Called by the main view's creator, this is the main initialization
|
||||
step for the list view.
|
||||
|
||||
It begins with a remote call to fetch the view's descriptors
|
||||
(``fields_view_get``).
|
||||
|
||||
Once the remote call is complete, the ``on_loaded`` even happens,
|
||||
holding three main operations:
|
||||
|
||||
:js:func:`~openerp.base.SearchView.make_widgets`
|
||||
|
||||
Builds and returns the top-level widgets of the search
|
||||
view. Because it returns an array of widget lines (a 2-dimensional
|
||||
matrix of widgets) it should be called recursively by container
|
||||
widgets (:js:class:`openerp.base.search.Group` for instance).
|
||||
|
||||
:js:func:`~openerp.base.search.Widget.render`
|
||||
|
||||
Called by the search view on all top-level widgets. Container
|
||||
widgets should recursively call this method on their own children
|
||||
widgets.
|
||||
|
||||
Widgets are provided with a mapping of ``{name: value}`` holding
|
||||
default values for the search view. They can freely pick their
|
||||
initial values from there, but must pass the mapping to their
|
||||
children widgets if they have any.
|
||||
|
||||
:js:func:`~openerp.base.search.Widget.start`
|
||||
|
||||
The last operation of the search view startup is to initialize all
|
||||
its widgets in order. This is again done recursively (the search
|
||||
view starts its children, which have to start their own children).
|
||||
|
||||
:js:func:`~openerp.base.SearchView.stop`
|
||||
|
||||
Used before discarding a search view, allows the search view to
|
||||
disable its events and pass the message to its own widgets,
|
||||
gracefully shutting down the whole view.
|
||||
|
||||
Widgets
|
||||
"""""""
|
||||
|
||||
In a search view, the widget is simply a unit of display.
|
||||
|
||||
All widgets must be able to react to three events, which will be
|
||||
called in this order:
|
||||
|
||||
:js:func:`~openerp.base.search.Widget.render`
|
||||
|
||||
Called with a map of default values. The widget must return a
|
||||
``String``, which is its HTML representation. That string can be
|
||||
empty (if the widget should not be represented).
|
||||
|
||||
Widgets are responsible for asking their children for rendering, and
|
||||
for passing along the default values.
|
||||
|
||||
:js:func:`~openerp.base.search.Widget.start`
|
||||
|
||||
Called without arguments. At this point, the widget has been fully
|
||||
rendered and can set its events up, if any.
|
||||
|
||||
The widget is responsible for starting its children, if it has any.
|
||||
|
||||
:js:func:`~openerp.base.search.Widget.stop`
|
||||
|
||||
Gives the widget the opportunity to unbind its events, remove itself
|
||||
from the DOM and perform any other cleanup task it may have.
|
||||
|
||||
Even if the widget does not do anything itself, it is responsible
|
||||
for shutting down its children.
|
||||
|
||||
An abstract type is available and can be inherited from, to simplify
|
||||
the implementation of those tasks:
|
||||
|
||||
.. TODO: insert Widget here
|
||||
|
||||
.. remember to document all methods
|
||||
|
||||
Inputs
|
||||
""""""
|
||||
|
||||
The search namespace (``openerp.base.search``) provides two more
|
||||
abstract types, used to implement input widgets:
|
||||
|
||||
* :js:class:`openerp.base.search.Input` is the most basic input type,
|
||||
it only implements :ref:`input registration
|
||||
<views-search-registration>`.
|
||||
|
||||
If inherited from, descendant classes should not call its
|
||||
implementations of :js:func:`~openerp.base.search.Input.get_context`
|
||||
and :js:func:`~openerp.base.search.Input.get_domain`.
|
||||
|
||||
* :js:class:`openerp.base.search.Field` is used to implement more
|
||||
"field" widgets (which allow the user to input potentially complex
|
||||
values).
|
||||
|
||||
It provides various services for its subclasses:
|
||||
|
||||
* Sets up the field attributes, using attributes from the field and
|
||||
the view node.
|
||||
|
||||
* It fills the widget with :js:class:`~openerp.base.search.Filter`
|
||||
if the field has any child filter.
|
||||
|
||||
* It automatically generates an identifier based on the field type
|
||||
and the field name, using
|
||||
:js:func:`~openerp.base.search.Widget.make_id`.
|
||||
|
||||
* It sets up a basic (overridable)
|
||||
:js:attr:`~openerp.base.search.Field.template` attribute, combined
|
||||
with the previous tasks, this makes subclasses of
|
||||
:js:class:`~openerp.base.search.Field` render themselves "for
|
||||
free".
|
||||
|
||||
* It provides basic implementations of ``get_context`` and
|
||||
``get_domain``, both hinging on the subclasses implementing
|
||||
``get_value()`` (which should return a correct, converted
|
||||
Javascript value):
|
||||
|
||||
:js:func:`~openerp.base.search.Field.get_context`
|
||||
|
||||
Checks if the field has a non-``null`` and non-empty
|
||||
(``String``) value, and that the field has a ``context`` attr.
|
||||
|
||||
If both conditions are fullfilled, returns the context.
|
||||
|
||||
:js:func:`~openerp.base.search.Field.get_domain`
|
||||
|
||||
Only requires that the field has a non-``null`` and non-empty
|
||||
value.
|
||||
|
||||
If the field has a ``filter_domain``, returns it
|
||||
immediately. Otherwise, builds a context using the field's
|
||||
name, the field :js:attr:`~openerp.base.search.Field.operator`
|
||||
and the field value, and returns it.
|
||||
|
||||
.. TODO: insert Input, Field, Filter, and just about every Field subclass
|
||||
|
||||
List View
|
||||
+++++++++
|
||||
|
||||
OpenERP Web's list views don't actually exist as such in OpenERP itself: a
|
||||
list view is an OpenERP tree view in the ``view_mode`` form.
|
||||
|
||||
The overall purpose of a list view is to display collections of objects in two
|
||||
main forms: per-object, where each object is a row in and of itself, and
|
||||
grouped, where multiple objects are represented with a single row providing
|
||||
an aggregated view of all grouped objects.
|
||||
|
||||
These two forms can be mixed within a single list view, if needed.
|
||||
|
||||
The root of a list view is :js:class:`openerp.base.ListView`, which may need
|
||||
to be overridden (partially or fully) to control list behavior in non-view
|
||||
cases (when using a list view as sub-component of a form widget for instance).
|
||||
|
||||
Creation and Initialization
|
||||
"""""""""""""""""""""""""""
|
||||
|
||||
As with most OpenERP Web views, the list view's
|
||||
:js:func:`~openerp.base.ListView.init` takes quite a number of arguments.
|
||||
|
||||
While most of them are the standard view constructor arguments
|
||||
(``view_manager``, ``session``, ``element_id``, ``dataset`` and an
|
||||
optional ``view_id``), the list view adds a number of options for basic
|
||||
customization (without having to override methods or templates):
|
||||
|
||||
``selectable`` (default: ``true``)
|
||||
Indicates that the list view should allow records to be selected
|
||||
individually. Displays selection check boxes to the left of all record rows,
|
||||
and allows for the triggering of the
|
||||
:ref:`selection event <listview-events-selection>`.
|
||||
``deletable`` (default: ``true``)
|
||||
Indicates that the list view should allow records to be removed
|
||||
individually. Displays a deletion button to the right of all record rows,
|
||||
and allows for the triggering of the
|
||||
:ref:`deletion event <listview-events-deletion>`.
|
||||
``header`` (default: ``true``)
|
||||
Indicates that list columns should bear a header sporting their name (for
|
||||
non-action columns).
|
||||
``addable`` (default: ``"New"``)
|
||||
Indicates that a record addition/creation button should be displayed in
|
||||
the list's header, along with its label. Also allows for the triggering of
|
||||
the :ref:`record addition event <listview-events-addition>`.
|
||||
``sortable`` (default: ``true``)
|
||||
Indicates that the list view can be sorted per-column (by clicking on its
|
||||
column headers).
|
||||
|
||||
.. TODO: event?
|
||||
``reorderable`` (default: ``true``)
|
||||
Indicates that the list view records can be reordered (and re-sequenced)
|
||||
by drag and drop.
|
||||
|
||||
.. TODO: event?
|
||||
|
||||
Events
|
||||
""""""
|
||||
.. _listview-events-addition:
|
||||
|
||||
Addition
|
||||
''''''''
|
||||
The addition event is used to add a record to an existing list view. The
|
||||
default behavior is to switch to the form view, on a new record.
|
||||
|
||||
Addition behavior can be overridden by replacing the
|
||||
:js:func:`~openerp.base.ListView.do_add_record` method.
|
||||
|
||||
.. _listview-events-selection:
|
||||
|
||||
Selection
|
||||
'''''''''
|
||||
The selection event is triggered when a given record is selected in the list
|
||||
view.
|
||||
|
||||
It can be overridden by replacing the
|
||||
:js:func:`~openerp.base.ListView.do_select` method.
|
||||
|
||||
The default behavior is simply to hide or display the list-wise deletion button
|
||||
depending on whether there are selected records or not.
|
||||
|
||||
.. _listview-events-deletion:
|
||||
|
||||
Deletion
|
||||
''''''''
|
||||
The deletion event is triggered when the user tries to remove 1..n records from
|
||||
the list view, either individually or globally (via the header button).
|
||||
|
||||
Deletion can be overridden by replacing the
|
||||
:js:func:`~openerp.base.ListView.do_delete` method. By default, this method
|
||||
calls :js:func:`~openerp.base.DataSet.unlink` in order to remove the records
|
||||
entirely.
|
||||
|
||||
.. note::
|
||||
|
||||
the list-wise deletion button (next to the record addition button)
|
||||
simply proxies to :js:func:`~openerp.base.ListView.do_delete` after
|
||||
obtaining all selected record ids, but it is possible to override it
|
||||
alone by replacing
|
||||
:js:func:`~openerp.base.ListView.do_delete_selected`.
|
||||
|
||||
Internal API Doc
|
||||
----------------
|
||||
|
||||
Python
|
||||
++++++
|
||||
|
||||
These classes should be moved to other sections of the doc as needed,
|
||||
probably.
|
||||
|
||||
.. automodule:: web.common.http
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
See also: :class:`~web.common.session.OpenERPSession`,
|
||||
:class:`~web.common.openerplib.main.OpenERPModel`
|
||||
|
||||
.. automodule:: web.controllers.main
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
||||
Python
|
||||
++++++
|
||||
|
||||
Testing for the OpenERP Web core is similar to :ref:`testing addons
|
||||
<addons-testing>`: the tests live in ``openerpweb.tests``, unittest2_
|
||||
is the testing framework and tests can be run via either unittest2
|
||||
(``unit2 discover``) or via nose_ (``nosetests``).
|
||||
|
||||
Tests for the OpenERP Web core can also be run using ``setup.py
|
||||
test``.
|
||||
|
||||
|
||||
.. _unittest2:
|
||||
http://www.voidspace.org.uk/python/articles/unittest2.shtml
|
||||
|
||||
.. _nose:
|
||||
http://somethingaboutorange.com/mrl/projects/nose/1.0.0/
|
|
@ -1,10 +0,0 @@
|
|||
Getting Started with OpenERP Web
|
||||
================================
|
||||
|
||||
Installing
|
||||
----------
|
||||
|
||||
.. per-distro packaging
|
||||
|
||||
Launching
|
||||
---------
|
|
@ -22,20 +22,6 @@ Contents:
|
|||
list-view
|
||||
form-notes
|
||||
|
||||
Older stuff
|
||||
-----------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
getting-started
|
||||
production
|
||||
widgets
|
||||
addons
|
||||
development
|
||||
project
|
||||
old-version
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
Main differences with the 6.0 client
|
||||
====================================
|
||||
|
||||
.. No more populate.sh, use virtualenvs
|
||||
|
||||
.. Logic is mainly in Javascript (had to make a choice between JS and
|
||||
.. Python logic)
|
||||
|
||||
.. Templating language changes
|
||||
|
||||
.. How to port addons and modules?
|
|
@ -1,47 +0,0 @@
|
|||
Deploying OpenERP Web
|
||||
=====================
|
||||
|
||||
.. After release one, add upgrade instructions if any
|
||||
|
||||
.. How about running the web client on alternative Python
|
||||
.. implementations e.g. pypy or Jython? Since the only lib with C
|
||||
.. accelerators we're using right now is SimpleJSON and it has a pure
|
||||
.. Python base component, we should be able to test and deploy on
|
||||
.. non-cpython no?
|
||||
|
||||
In-depth configuration
|
||||
----------------------
|
||||
|
||||
SSL, basic proxy (link to relevant section), links to sections and
|
||||
example files for various servers and proxies, WSGI
|
||||
integration/explanation (if any), ...
|
||||
|
||||
Deployment Options
|
||||
------------------
|
||||
|
||||
Serving via WSGI
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Apache mod_wsgi
|
||||
+++++++++++++++
|
||||
|
||||
NGinx mod_wsgi
|
||||
++++++++++++++
|
||||
|
||||
uWSGI
|
||||
+++++
|
||||
|
||||
Gunicorn
|
||||
++++++++
|
||||
|
||||
FastCGI, SCGI, or AJP
|
||||
+++++++++++++++++++++
|
||||
|
||||
Behind a proxy
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Apache mod_proxy
|
||||
++++++++++++++++
|
||||
|
||||
NGinx HttpProxy
|
||||
+++++++++++++++
|
451
doc/project.rst
451
doc/project.rst
|
@ -1,451 +0,0 @@
|
|||
The OpenERP Web open-source project
|
||||
===================================
|
||||
|
||||
Getting involved
|
||||
----------------
|
||||
|
||||
Translations
|
||||
++++++++++++
|
||||
|
||||
Bug reporting
|
||||
+++++++++++++
|
||||
|
||||
Source code repository
|
||||
++++++++++++++++++++++
|
||||
|
||||
Merge proposals
|
||||
+++++++++++++++
|
||||
|
||||
Coding issues and coding conventions
|
||||
++++++++++++++++++++++++++++++++++++
|
||||
|
||||
Javascript coding
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
These are a number of guidelines for javascript code. More than coding
|
||||
conventions, these are warnings against potentially harmful or sub-par
|
||||
constructs.
|
||||
|
||||
Ideally, you should be able to configure your editor or IDE to warn you against
|
||||
these kinds of issues.
|
||||
|
||||
Use ``var`` for *all* declarations
|
||||
**********************************
|
||||
|
||||
In javascript (as opposed to Python), assigning to a variable which does not
|
||||
already exist and is not explicitly declared (via ``var``) will implicitly
|
||||
create a global variable. This is bad for a number of reasons:
|
||||
|
||||
* It leaks information outside function scopes
|
||||
* It keeps memory of previous run, with potentially buggy behaviors
|
||||
* It may conflict with other functions with the same issue
|
||||
* It makes code harder to statically check (via e.g. IDE inspectors)
|
||||
|
||||
.. note::
|
||||
It is perfectly possible to use ``var`` in ``for`` loops:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
for (var i = 0; i < some_array.length; ++i) {
|
||||
// code here
|
||||
}
|
||||
|
||||
this is not an issue
|
||||
|
||||
All local *and global* variables should be declared via ``var``.
|
||||
|
||||
.. note:: generally speaking, you should not need globals in OpenERP Web: you
|
||||
can just declare a variable local to your top-level function. This
|
||||
way, if your widget/addon is instantiated several times on the same
|
||||
page (because it's used in embedded mode) each instance will have its
|
||||
own internal but global-to-its-objects data.
|
||||
|
||||
Do not leave trailing commas in object literals
|
||||
***********************************************
|
||||
|
||||
While it is legal to leave trailing commas in Python dictionaries, e.g.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
foo = {
|
||||
'a': 1,
|
||||
'b': 2,
|
||||
}
|
||||
|
||||
and it's valid in ECMAScript 5 and most browsers support it in Javascript, you
|
||||
should *never* use trailing commas in Javascript object literals:
|
||||
|
||||
* Internet Explorer does *not* support trailing commas (at least until and
|
||||
including Internet Explorer 8), and trailing comma will cause hard-to-debug
|
||||
errors in it
|
||||
|
||||
* JSON does not accept trailing comma (it is a syntax error), and using them
|
||||
in object literals puts you at risks of using them in literal JSON strings
|
||||
as well (though there are few reasons to write JSON by hand)
|
||||
|
||||
*Never* use ``for … in`` to iterate on arrays
|
||||
*********************************************
|
||||
|
||||
:ref:`Iterating over an object with for…in is a bit tricky already
|
||||
<for-in-iteration>`, it is far more complex than in Python (where it Just
|
||||
Works™) due to the interaction of various Javascript features, but to iterate
|
||||
on arrays it becomes downright deadly and errorneous: ``for…in`` really
|
||||
iterates over an *object*'s *properties*.
|
||||
|
||||
With an array, this has the following consequences:
|
||||
|
||||
* It does not necessarily iterate in numerical order, nor does it iterate in
|
||||
any kind of set order. The order is implementation-dependent and may vary
|
||||
from one run to the next depending on a number of reasons and implementation
|
||||
details.
|
||||
* If properties are added to an array, to ``Array.prototype`` or to
|
||||
``Object.prototype`` (the latter two should not happen in well-behaved
|
||||
javascript code, but you never know...) those properties *will* be iterated
|
||||
over by ``for…in``. While ``Object.hasOwnProperty`` will guard against
|
||||
iterating prototype properties, they will not guard against properties set
|
||||
on the array instance itself (as memoizers for instance).
|
||||
|
||||
Note that this includes setting negative keys on arrays.
|
||||
|
||||
For this reason, ``for…in`` should **never** be used on array objects. Instead,
|
||||
you should use either a normal ``for`` or (even better, unless you have
|
||||
profiled the code and found a hotspot) one of Underscore's array iteration
|
||||
methods (`_.each`_, `_.map`_, `_.filter`_, etc...).
|
||||
|
||||
Underscore is guaranteed to be bundled and available in OpenERP Web scopes.
|
||||
|
||||
.. _for-in-iteration:
|
||||
|
||||
Use ``hasOwnProperty`` when iterating on an object with ``for … in``
|
||||
********************************************************************
|
||||
|
||||
``for…in`` is Javascript's built-in facility for iterating over and object's
|
||||
properties.
|
||||
|
||||
`It is also fairly tricky to use`_: it iterates over *all* non-builtin
|
||||
properties of your objects [#]_, which includes methods of an object's class.
|
||||
|
||||
As a result, when iterating over an object with ``for…in`` the first line of
|
||||
the body *should* generally be a call to `Object.hasOwnProperty`_. This call
|
||||
will check whether the property was set directly on the object or comes from
|
||||
the object's class:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
for(var key in ob) {
|
||||
if (!ob.hasOwnProperty(key)) {
|
||||
// comes from ob's class
|
||||
continue;
|
||||
}
|
||||
// do stuff with key
|
||||
}
|
||||
|
||||
Since properties can be added directly to e.g. ``Object.prototype`` (even
|
||||
though it's usually considered bad style), you should not assume you ever know
|
||||
which properties ``for…in`` is going to iterate over.
|
||||
|
||||
An alternative is to use Underscore's iteration methods, which generally work
|
||||
over objects as well as arrays:
|
||||
|
||||
Instead of
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
for (var key in ob) {
|
||||
if (!ob.hasOwnProperty(key)) { continue; }
|
||||
var value = ob[key];
|
||||
// Do stuff with key and value
|
||||
}
|
||||
|
||||
you could write:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
_.each(ob, function (value, key) {
|
||||
// do stuff with key and value
|
||||
});
|
||||
|
||||
and not worry about the details of the iteration: underscore should do the
|
||||
right thing for you on its own [#]_.
|
||||
|
||||
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:
|
||||
|
||||
.. code-block:: restructuredtext
|
||||
|
||||
: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 using the ``:type:`` field
|
||||
|
||||
.. code-block:: restructuredtext
|
||||
|
||||
:param foo: what the purpose of foo is
|
||||
:type foo: some.addon.Class
|
||||
|
||||
Mentions of other methods (including within the same class), modules
|
||||
or types in descriptions (of anything, including parameters) 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 docstring via the ``.. attribute::`` directive, 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:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
/** @namespace */
|
||||
foo.bar.baz = {};
|
||||
|
||||
while for a class it is:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
/** @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
|
||||
---------------------
|
||||
|
||||
Release process
|
||||
+++++++++++++++
|
||||
|
||||
OpenSUSE packaging: http://blog.lowkster.com/2011/04/packaging-python-packages-in-opensuse.html
|
||||
|
||||
Roadmap
|
||||
+++++++
|
||||
|
||||
Release notes
|
||||
+++++++++++++
|
||||
|
||||
.. [#] More precisely, it iterates over all *enumerable* properties. It just
|
||||
happens that built-in properties (such as ``String.indexOf`` or
|
||||
``Object.toString``) are set to non-enumerable.
|
||||
|
||||
The enumerability of a property can be checked using
|
||||
`Object.propertyIsEnumeable`_.
|
||||
|
||||
Before ECMAScript 5, it was not possible for user-defined properties
|
||||
to be non-enumerable in a portable manner. ECMAScript 5 introduced
|
||||
`Object.defineProperty`_ which lets user code create non-enumerable
|
||||
properties (and more, read-only properties for instance, or implicit
|
||||
getters and setters). However, support for these is not fully complete
|
||||
at this point, and they are not being used in OpenERP Web code anyway.
|
||||
|
||||
.. [#] While using underscore is generally the preferred method (simpler,
|
||||
more reliable and easier to write than a *correct* ``for…in``
|
||||
iteration), it is also probably slower (due to the overhead of
|
||||
calling a bunch of functions).
|
||||
|
||||
As a result, if you profile some code and find out that an underscore
|
||||
method adds unacceptable overhead in a tight loop, you may want to
|
||||
replace it with a ``for…in`` (or a regular ``for`` statement for
|
||||
arrays).
|
||||
|
||||
.. [#] 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/
|
||||
.. _\_.each:
|
||||
http://documentcloud.github.com/underscore/#each
|
||||
.. _\_.map:
|
||||
http://documentcloud.github.com/underscore/#map
|
||||
.. _\_.filter:
|
||||
http://documentcloud.github.com/underscore/#select
|
||||
.. _It is also fairly tricky to use:
|
||||
https://developer.mozilla.org/en/JavaScript/Reference/Statements/for...in#Description
|
||||
.. _Object.propertyIsEnumeable:
|
||||
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/propertyIsEnumerable
|
||||
.. _Object.defineProperty:
|
||||
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty
|
||||
.. _Object.hasOwnProperty:
|
||||
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/hasOwnProperty
|
|
@ -1,12 +0,0 @@
|
|||
OpenERP Web as a widgets provider
|
||||
=================================
|
||||
|
||||
* Using a readonly view as a widget
|
||||
|
||||
* Site example
|
||||
* iGoogle example
|
||||
* social site example e.g. Facebook app?
|
||||
|
||||
* Write-access widgets (e.g. contact form)
|
||||
* Multiple widgets on the same page
|
||||
* JSON-RPC2 API description for third-parties?
|
Loading…
Reference in New Issue