diff options
| author | Openerp Online <online@openerp.com> | 2012-03-05 13:40:16 +0100 |
|---|---|---|
| committer | Openerp Online <online@openerp.com> | 2012-03-05 13:40:16 +0100 |
| commit | 0c8e11643f84afddec07f21b6f7fc2b981fb2706 (patch) | |
| tree | 73e29a0f5409688fdcc513b488c169dba2febdfd /doc/development.rst | |
| parent | ba3193dd4d5aa2a138899c2ac55b3e7fa7768256 (diff) | |
[DOC] use a flat (i.e. no source/ directory) structure.
bzr revid: online@openerp.com-20120305124016-d25rncl3q2qcem31
Diffstat (limited to 'doc/development.rst')
| -rw-r--r-- | doc/development.rst | 424 |
1 files changed, 424 insertions, 0 deletions
diff --git a/doc/development.rst b/doc/development.rst new file mode 100644 index 00000000000..7431a3e16de --- /dev/null +++ b/doc/development.rst @@ -0,0 +1,424 @@ +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/ |
